您现在的位置是:首页 >其他 >[游戏开发][Unity]Assetbundle加载篇(4)检查断点续传以及开始下载AB包网站首页其他
[游戏开发][Unity]Assetbundle加载篇(4)检查断点续传以及开始下载AB包
简介[游戏开发][Unity]Assetbundle加载篇(4)检查断点续传以及开始下载AB包
下载AB包之前,要检查该AB包是否下载中断过,例如用户杀程序,卡死等情况。
前文有讲解过,下载AB包会先下载到临时文件夹,全部下载成功后,全部剪切到persistentDataPath沙盒目录中。
回顾一下之前的筛选机制,哪些AB包可以加入下载列表,其中并没有临时下载目录的判断。
该AB包数据是否加入列表要经过下面几个筛选
判断persistentDataPath沙盒目录是否存在该AB包,如果存在证明之前下载过,无需加入下载列表。
判断StreamingAsset目录中是否存在该AB包,如果存在证明打包时该AB包已经在包体里,无需加入下载列表。
如果该AB包的下载类型是游戏内下载,或者叫边玩边下,那么不需要在游戏启动热更时下载,无需加入下载列表
检查是否在临时下载目录中是否存在的代码如下
private IEnumerator CheckTempFileComplete(List<PatchElement> list, bool isPostCheck)
{
PatchEventDispatcher.SendCheckDownloadedFileMd5();
var sw = new Stopwatch();
sw.Start();
var buf = new byte[10240];
this.totalDownloadSizeKB = 0;
this.currentDownloadSizeKB = 0;
this.currentDownloadCount = 0;
using (var md5 = System.Security.Cryptography.MD5.Create())
{
foreach (var ele in list)
{
this.totalDownloadSizeKB += ele.SizeKB;
string savePath = AssetPathHelper.MakeDownloadTempPath(ele.Name);
if (!File.Exists(savePath))
{
//下载后的检查需要抛出异常,下载前的检查跳过不存在文件
if (isPostCheck)
{
PatchHelper.Log(ELogLevel.Error, $"[checking md5] file is not existed: {ele.Name}");
failedOnCheckDownload = true;
yield break;
}
else
{
continue;
}
}
using (var fs = new FileStream(savePath, FileMode.Open, FileAccess.Read))
{
int byteRead;
md5.Initialize();
while ((byteRead = fs.Read(buf, 0, buf.Length)) > 0)
{
md5.TransformBlock(buf, 0, byteRead, null, 0);
if (sw.ElapsedMilliseconds > 250)
{
yield return null;
sw.Restart();
}
}
md5.TransformFinalBlock(buf, 0, 0);
fs.Close();
string localMd5 = BitConverter.ToString(md5.Hash).Replace("-", "");
if (string.Equals(ele.MD5, localMd5, StringComparison.OrdinalIgnoreCase))
{
//MotionLog.Log(ELogLevel.Log, StringFormat.Format("skip download existed file: {0}", savePath));
ele.SkipDownload = true;
this.currentDownloadSizeKB += ele.SizeKB;
this.currentDownloadCount++;
}
else if (isPostCheck)
{
PatchHelper.Log(ELogLevel.Error, $"Web file md5 verification error : {ele.Name}");
PatchHelper.Log(ELogLevel.Error, $"local md5 is : {localMd5}");
PatchHelper.Log(ELogLevel.Error, $"md5 in manifest is : {ele.MD5}");
PatchEventDispatcher.SendWebFileMD5VerifyFailedMsg(ele.Name);
failedOnCheckDownload = true;
File.Delete(savePath);
yield break;
}
}
}
}
}
布尔值isPostCheck的作用是是否是二次检测,先不管它
按流程来走
检查该AB包是否在临时文件夹中存在,如果不存在则跳过
如果已存在,创建该临时下载文件的MD5,与下载清单中的MD5做对比
如果MD5对比一致,则标记该AB包数据为SkipDownload,同时标记下载数据长度,供UI显示
我们项目里目前的代码被人改了,我也有有点看不懂为何要用 md5.TransformBlock的方式
下面是一套生成MD5码的代码,加载文件,MD5CryptoServiceProvider会根据加载的stream生成hash,
readonly MD5CryptoServiceProvider _provider = new MD5CryptoServiceProvider();
public static string StreamMD5()
{
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
{
byte[] hashBytes = _provider.ComputeHash(stream);
return ToString(hashBytes);
}
}
private static string ToString(byte[] hashBytes)
{
var sb = new StringBuilder();
foreach (var t in hashBytes)
sb.Append(t.ToString("x2"));
return sb.ToString();
}
首先要判断临时下载目录中是否存在该文件,如果没有,我们项目
private IEnumerator Download()
{
// 计算下载文件的总大小
totalDownloadCount = _patcher.DownloadList.Count;
if (totalDownloadCount == 0)
{
_patcher.SwitchNext();
yield break;
}
//先检查一遍temp目录有没有下载完的, 计算这次实际需要下载的个数和size
yield return CheckTempFileComplete(_patcher.DownloadList, false);
// 开始下载列表里的所有资源
PatchHelper.Log(ELogLevel.Log, $"Begine download web files : {totalDownloadCount-currentDownloadCount}");
PatchEventDispatcher.SendPatchStatesChangeMsg(EPatchStates.DownloadWebFiles);
var startTime = Time.realtimeSinceStartup;
var newDownloaded = new List<PatchElement>(_patcher.DownloadList.Count);
foreach (var element in _patcher.DownloadList)
{
if (element.SkipDownload) continue;
newDownloaded.Add(element);
}
if (useMultiRequest)
{
yield return DownloadWithMultiTask(newDownloaded);
}
else
{
yield return DownloadWithSingleTask(newDownloaded);
}
if (failedOnDownload)
{
yield break;
}
MotionLog.Log(ELogLevel.Log, $"<color=#ff0000>Downloading {newDownloaded.Count} files cost {Time.realtimeSinceStartup-startTime} sec.</color>");
//全部下载完成后把这次新下载的文件再校验一遍,如果有文件失败就退出
yield return CheckTempFileComplete(newDownloaded, true);
if (failedOnCheckDownload)
{
yield break;
}
if (_patcher.MiniAndroid)
{
var fileCount = _patcher.DownloadList[0].Version; // tricky storing filecount in field [version]
var zipFileName = _patcher.DownloadList[0].Name;
yield return DecompressInitPack(zipFileName, fileCount);
}
else
{
yield return DeployDownloadFiles();
}
// 最后清空下载列表
_patcher.DownloadList.Clear();
_patcher.SwitchNext();
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。