diff --git a/Assets/YooAsset/Runtime/CacheSystem/CacheSystem.cs b/Assets/YooAsset/Runtime/CacheSystem/CacheSystem.cs index 3ee5b92..726ab84 100644 --- a/Assets/YooAsset/Runtime/CacheSystem/CacheSystem.cs +++ b/Assets/YooAsset/Runtime/CacheSystem/CacheSystem.cs @@ -73,9 +73,9 @@ namespace YooAsset } /// - /// 验证并缓存补丁包文件 + /// 验证并缓存本地文件 /// - public static EVerifyResult VerifyAndCacheBundle(PatchBundle patchBundle, EVerifyLevel verifyLevel) + public static EVerifyResult VerifyAndCacheLocalBundleFile(PatchBundle patchBundle, EVerifyLevel verifyLevel) { var verifyResult = VerifyContentInternal(patchBundle.CachedFilePath, patchBundle.FileSize, patchBundle.FileCRC, verifyLevel); if (verifyResult == EVerifyResult.Succeed) @@ -83,10 +83,41 @@ namespace YooAsset return verifyResult; } + /// + /// 验证并缓存下载文件 + /// + public static EVerifyResult VerifyAndCacheDownloadBundleFile(PatchBundle patchBundle, EVerifyLevel verifyLevel) + { + string tempFilePath = patchBundle.CachedFilePath + ".temp"; + var verifyResult = VerifyContentInternal(tempFilePath, patchBundle.FileSize, patchBundle.FileCRC, verifyLevel); + if (verifyResult == EVerifyResult.Succeed) + { + try + { + string destFilePath = patchBundle.CachedFilePath; + if (File.Exists(destFilePath)) + File.Delete(destFilePath); + + FileInfo fileInfo = new FileInfo(tempFilePath); + fileInfo.MoveTo(destFilePath); + } + catch (Exception) + { + verifyResult = EVerifyResult.FileMoveFailed; + } + + if (verifyResult == EVerifyResult.Succeed) + { + CacheBundle(patchBundle); + } + } + return verifyResult; + } + /// /// 验证文件完整性 /// - public static EVerifyResult VerifyContentInternal(string filePath, long fileSize, string fileCRC, EVerifyLevel verifyLevel) + private static EVerifyResult VerifyContentInternal(string filePath, long fileSize, string fileCRC, EVerifyLevel verifyLevel) { try { diff --git a/Assets/YooAsset/Runtime/CacheSystem/CacheVerifier.cs b/Assets/YooAsset/Runtime/CacheSystem/CacheVerifier.cs index 67ceee9..fd97b20 100644 --- a/Assets/YooAsset/Runtime/CacheSystem/CacheVerifier.cs +++ b/Assets/YooAsset/Runtime/CacheSystem/CacheVerifier.cs @@ -161,7 +161,7 @@ namespace YooAsset private void VerifyFileWithoutThread(VerifyInfo verifyIno) { - var verifyResult = CacheSystem.VerifyAndCacheBundle(verifyIno.VerifyBundle, CacheSystem.InitVerifyLevel); + var verifyResult = CacheSystem.VerifyAndCacheLocalBundleFile(verifyIno.VerifyBundle, CacheSystem.InitVerifyLevel); if (verifyResult == EVerifyResult.Succeed) { VerifySuccessList.Add(verifyIno); diff --git a/Assets/YooAsset/Runtime/CacheSystem/EVerifyResult.cs b/Assets/YooAsset/Runtime/CacheSystem/EVerifyResult.cs index 6d4b48f..8179f08 100644 --- a/Assets/YooAsset/Runtime/CacheSystem/EVerifyResult.cs +++ b/Assets/YooAsset/Runtime/CacheSystem/EVerifyResult.cs @@ -9,7 +9,12 @@ namespace YooAsset /// /// 文件不存在 /// - FileNotExisted = -4, + FileNotExisted = -5, + + /// + /// 文件移动失败(重命名失败) + /// + FileMoveFailed = -4, /// /// 文件内容不足(小于正常大小) diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/DownloaderBase.cs b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/DownloaderBase.cs index bcd04dc..f83031e 100644 --- a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/DownloaderBase.cs +++ b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/DownloaderBase.cs @@ -6,9 +6,12 @@ namespace YooAsset protected enum ESteps { None, - CheckLocalFile, - CreateDownload, + CheckTempFile, + PrepareDownload, + CreateResumeDownloader, + CreateGeneralDownloader, CheckDownload, + VerifyDownload, TryAgain, Succeed, Failed, @@ -56,7 +59,7 @@ namespace YooAsset { _failedTryAgain = failedTryAgain; _timeout = timeout; - _steps = ESteps.CheckLocalFile; + _steps = ESteps.CheckTempFile; } } public abstract void Update(); diff --git a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileDownloader.cs b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileDownloader.cs index a80b403..fc5b3d4 100644 --- a/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileDownloader.cs +++ b/Assets/YooAsset/Runtime/DownloadSystem/Downloader/FileDownloader.cs @@ -10,6 +10,7 @@ namespace YooAsset internal sealed class FileDownloader : DownloaderBase { private readonly bool _breakResume; + private readonly string _tempFilePath; private UnityWebRequest _webRequest = null; private DownloadHandlerFileRange _downloadHandle = null; @@ -24,6 +25,7 @@ namespace YooAsset public FileDownloader(BundleInfo bundleInfo, bool breakResume) : base(bundleInfo) { _breakResume = breakResume; + _tempFilePath = bundleInfo.Bundle.CachedFilePath + ".temp"; } public override void Update() { @@ -33,9 +35,9 @@ namespace YooAsset return; // 检测本地文件 - if (_steps == ESteps.CheckLocalFile) + if (_steps == ESteps.CheckTempFile) { - var verifyResult = CacheSystem.VerifyAndCacheBundle(_bundleInfo.Bundle, EVerifyLevel.High); + var verifyResult = CacheSystem.VerifyAndCacheDownloadBundleFile(_bundleInfo.Bundle, EVerifyLevel.High); if (verifyResult == EVerifyResult.Succeed) { _steps = ESteps.Succeed; @@ -44,19 +46,16 @@ namespace YooAsset { if (verifyResult == EVerifyResult.FileOverflow) { - string cacheFilePath = _bundleInfo.Bundle.CachedFilePath; - if (File.Exists(cacheFilePath)) - File.Delete(cacheFilePath); + if (File.Exists(_tempFilePath)) + File.Delete(_tempFilePath); } - _steps = ESteps.CreateDownload; + _steps = ESteps.PrepareDownload; } } // 创建下载器 - if (_steps == ESteps.CreateDownload) + if (_steps == ESteps.PrepareDownload) { - string fileSavePath = _bundleInfo.Bundle.CachedFilePath; - // 重置变量 _downloadProgress = 0f; _downloadedBytes = 0; @@ -66,53 +65,73 @@ namespace YooAsset _latestDownloadRealtime = Time.realtimeSinceStartup; _tryAgainTimer = 0f; - // 是否开启断点续传下载 - if (_breakResume) - { - long fileLength = -1; - if (File.Exists(fileSavePath)) - { - FileInfo fileInfo = new FileInfo(fileSavePath); - fileLength = fileInfo.Length; - _fileOriginLength = (ulong)fileLength; - _downloadedBytes = _fileOriginLength; - } + // 获取请求地址 + _requestURL = GetRequestURL(); - _requestURL = GetRequestURL(); - _webRequest = UnityWebRequest.Get(_requestURL); + if (_breakResume) + _steps = ESteps.CreateResumeDownloader; + else + _steps = ESteps.CreateGeneralDownloader; + } + + // 创建普通的下载器 + if (_steps == ESteps.CreateGeneralDownloader) + { + if (File.Exists(_tempFilePath)) + { + File.Delete(_tempFilePath); + } + + _webRequest = new UnityWebRequest(_requestURL, UnityWebRequest.kHttpVerbGET); + DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath); + handler.removeFileOnAbort = true; + _webRequest.downloadHandler = handler; + _webRequest.disposeDownloadHandlerOnDispose = true; + + if (DownloadSystem.CertificateHandlerInstance != null) + { + _webRequest.certificateHandler = DownloadSystem.CertificateHandlerInstance; + _webRequest.disposeCertificateHandlerOnDispose = false; + } + + _webRequest.SendWebRequest(); + _steps = ESteps.CheckDownload; + } + + // 创建断点续传下载器 + if (_steps == ESteps.CreateResumeDownloader) + { + long fileLength = -1; + if (File.Exists(_tempFilePath)) + { + FileInfo fileInfo = new FileInfo(_tempFilePath); + fileLength = fileInfo.Length; + _fileOriginLength = (ulong)fileLength; + _downloadedBytes = _fileOriginLength; + } #if UNITY_2019_4_OR_NEWER - var handler = new DownloadHandlerFile(fileSavePath, true); - handler.removeFileOnAbort = false; + _webRequest = new UnityWebRequest(_requestURL, UnityWebRequest.kHttpVerbGET); + var handler = new DownloadHandlerFile(_tempFilePath, true); + handler.removeFileOnAbort = false; #else - var handler = new DownloadHandlerFileRange(fileSavePath, _bundleInfo.Bundle.FileSize, _webRequest); - _downloadHandle = handler; + _webRequest = new UnityWebRequest(_requestURL, UnityWebRequest.kHttpVerbGET); + var handler = new DownloadHandlerFileRange(_tempFilePath, _bundleInfo.Bundle.FileSize, _webRequest); + _downloadHandle = handler; #endif + _webRequest.downloadHandler = handler; + _webRequest.disposeDownloadHandlerOnDispose = true; + if (fileLength > 0) + _webRequest.SetRequestHeader("Range", $"bytes={fileLength}-"); - if (DownloadSystem.CertificateHandlerInstance != null) - { - _webRequest.certificateHandler = DownloadSystem.CertificateHandlerInstance; - _webRequest.disposeCertificateHandlerOnDispose = false; - } - - _webRequest.downloadHandler = handler; - _webRequest.disposeDownloadHandlerOnDispose = true; - if (fileLength > 0) - _webRequest.SetRequestHeader("Range", $"bytes={fileLength}-"); - _webRequest.SendWebRequest(); - _steps = ESteps.CheckDownload; - } - else + if (DownloadSystem.CertificateHandlerInstance != null) { - _requestURL = GetRequestURL(); - _webRequest = new UnityWebRequest(_requestURL, UnityWebRequest.kHttpVerbGET); - DownloadHandlerFile handler = new DownloadHandlerFile(fileSavePath); - handler.removeFileOnAbort = true; - _webRequest.downloadHandler = handler; - _webRequest.disposeDownloadHandlerOnDispose = true; - _webRequest.SendWebRequest(); - _steps = ESteps.CheckDownload; + _webRequest.certificateHandler = DownloadSystem.CertificateHandlerInstance; + _webRequest.disposeCertificateHandlerOnDispose = false; } + + _webRequest.SendWebRequest(); + _steps = ESteps.CheckDownload; } // 检测下载结果 @@ -145,78 +164,77 @@ namespace YooAsset } #endif - // 检查文件完整性 - if (hasError == false) - { - var verifyResult = CacheSystem.VerifyAndCacheBundle(_bundleInfo.Bundle, EVerifyLevel.High); - if (verifyResult != EVerifyResult.Succeed) - { - hasError = true; - _lastError = $"Verify bundle content failed : {_bundleInfo.Bundle.FileName}"; - _lastCode = _webRequest.responseCode; - - // 验证失败后删除文件 - string cacheFilePath = _bundleInfo.Bundle.CachedFilePath; - if (File.Exists(cacheFilePath)) - File.Delete(cacheFilePath); - } - } - - // 如果下载失败 + // 如果网络异常 if (hasError) { - // 注意:非断点续传下载失败之后删除文件 - if (_breakResume == false) - { - string cacheFilePath = _bundleInfo.Bundle.CachedFilePath; - if (File.Exists(cacheFilePath)) - File.Delete(cacheFilePath); - } - else + if (_breakResume) { // 注意:下载断点续传文件发生特殊错误码之后删除文件 if (DownloadSystem.ClearFileResponseCodes != null) { if (DownloadSystem.ClearFileResponseCodes.Contains(_webRequest.responseCode)) { - string cacheFilePath = _bundleInfo.Bundle.CachedFilePath; - if (File.Exists(cacheFilePath)) - File.Delete(cacheFilePath); + if (File.Exists(_tempFilePath)) + File.Delete(_tempFilePath); } } } - - // 失败后重新尝试 - if (_failedTryAgain > 0) - { - ReportWarning(); - _steps = ESteps.TryAgain; - } else { - ReportError(); - _steps = ESteps.Failed; + // 注意:非断点续传下载失败之后删除文件 + if (File.Exists(_tempFilePath)) + File.Delete(_tempFilePath); } + + _steps = ESteps.TryAgain; } else { - _lastError = string.Empty; - _lastCode = 0; - _steps = ESteps.Succeed; + _steps = ESteps.VerifyDownload; } // 释放下载器 DisposeWebRequest(); } + // 验证下载文件 + if (_steps == ESteps.VerifyDownload) + { + var verifyResult = CacheSystem.VerifyAndCacheDownloadBundleFile(_bundleInfo.Bundle, EVerifyLevel.High); + if (verifyResult == EVerifyResult.Succeed) + { + _lastError = string.Empty; + _lastCode = 0; + _steps = ESteps.Succeed; + } + else + { + _lastError = $"Verify bundle content failed : {_bundleInfo.Bundle.FileName}"; + + // 验证失败后删除文件 + if (File.Exists(_tempFilePath)) + File.Delete(_tempFilePath); + + _steps = ESteps.TryAgain; + } + } + // 重新尝试下载 if (_steps == ESteps.TryAgain) { + if (_failedTryAgain <= 0) + { + ReportError(); + _steps = ESteps.Failed; + return; + } + _tryAgainTimer += Time.unscaledDeltaTime; if (_tryAgainTimer > 1f) { _failedTryAgain--; - _steps = ESteps.CreateDownload; + _steps = ESteps.PrepareDownload; + ReportWarning(); YooLogger.Warning($"Try again download : {_requestURL}"); } }