From 32d422aeaa858127e77fc84e63dfb237e1d118a3 Mon Sep 17 00:00:00 2001 From: hevinci Date: Tue, 29 Mar 2022 18:14:04 +0800 Subject: [PATCH] Update DownloadSystem --- .../Download.meta => DownloadSystem.meta} | 0 .../DownloadSystem.cs | 0 .../DownloadSystem.cs.meta | 0 .../Runtime/DownloadSystem/DownloaderBase.cs | 122 ++++++++ .../DownloadSystem/DownloaderBase.cs.meta | 11 + .../FileDownloader.cs | 146 ++-------- .../FileDownloader.cs.meta | 0 .../Runtime/DownloadSystem/HttpDownloader.cs | 243 ++++++++++++++++ .../HttpDownloader.cs.meta | 0 .../ThreadSyncContext.cs | 0 .../ThreadSyncContext.cs.meta | 0 .../UnityWebDataRequester.cs | 0 .../UnityWebDataRequester.cs.meta | 0 .../UnityWebFileRequester.cs | 0 .../UnityWebFileRequester.cs.meta | 0 .../PatchSystem/Download/HttpDownloader.cs | 266 ------------------ 16 files changed, 402 insertions(+), 386 deletions(-) rename Assets/YooAsset/Runtime/{PatchSystem/Download.meta => DownloadSystem.meta} (100%) rename Assets/YooAsset/Runtime/{PatchSystem/Download => DownloadSystem}/DownloadSystem.cs (100%) rename Assets/YooAsset/Runtime/{PatchSystem/Download => DownloadSystem}/DownloadSystem.cs.meta (100%) create mode 100644 Assets/YooAsset/Runtime/DownloadSystem/DownloaderBase.cs create mode 100644 Assets/YooAsset/Runtime/DownloadSystem/DownloaderBase.cs.meta rename Assets/YooAsset/Runtime/{PatchSystem/Download => DownloadSystem}/FileDownloader.cs (53%) rename Assets/YooAsset/Runtime/{PatchSystem/Download => DownloadSystem}/FileDownloader.cs.meta (100%) create mode 100644 Assets/YooAsset/Runtime/DownloadSystem/HttpDownloader.cs rename Assets/YooAsset/Runtime/{PatchSystem/Download => DownloadSystem}/HttpDownloader.cs.meta (100%) rename Assets/YooAsset/Runtime/{PatchSystem/Download => DownloadSystem}/ThreadSyncContext.cs (100%) rename Assets/YooAsset/Runtime/{PatchSystem/Download => DownloadSystem}/ThreadSyncContext.cs.meta (100%) rename Assets/YooAsset/Runtime/{PatchSystem/Download => DownloadSystem}/UnityWebDataRequester.cs (100%) rename Assets/YooAsset/Runtime/{PatchSystem/Download => DownloadSystem}/UnityWebDataRequester.cs.meta (100%) rename Assets/YooAsset/Runtime/{PatchSystem/Download => DownloadSystem}/UnityWebFileRequester.cs (100%) rename Assets/YooAsset/Runtime/{PatchSystem/Download => DownloadSystem}/UnityWebFileRequester.cs.meta (100%) delete mode 100644 Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download.meta b/Assets/YooAsset/Runtime/DownloadSystem.meta similarity index 100% rename from Assets/YooAsset/Runtime/PatchSystem/Download.meta rename to Assets/YooAsset/Runtime/DownloadSystem.meta diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs b/Assets/YooAsset/Runtime/DownloadSystem/DownloadSystem.cs similarity index 100% rename from Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs rename to Assets/YooAsset/Runtime/DownloadSystem/DownloadSystem.cs diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/DownloadSystem.cs.meta similarity index 100% rename from Assets/YooAsset/Runtime/PatchSystem/Download/DownloadSystem.cs.meta rename to Assets/YooAsset/Runtime/DownloadSystem/DownloadSystem.cs.meta diff --git a/Assets/YooAsset/Runtime/DownloadSystem/DownloaderBase.cs b/Assets/YooAsset/Runtime/DownloadSystem/DownloaderBase.cs new file mode 100644 index 0000000..359eec3 --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/DownloaderBase.cs @@ -0,0 +1,122 @@ + +namespace YooAsset +{ + internal abstract class DownloaderBase + { + protected enum ESteps + { + None, + CreateDownload, + CheckDownload, + TryAgain, + Succeed, + Failed, + } + + protected readonly BundleInfo _bundleInfo; + + protected ESteps _steps = ESteps.None; + + protected int _timeout; + protected int _failedTryAgain; + protected int _requestCount; + protected string _requestURL; + + protected string _lastError = string.Empty; + protected float _downloadProgress = 0f; + protected ulong _downloadedBytes = 0; + + + /// + /// 下载进度(0-100f) + /// + public float DownloadProgress + { + get { return _downloadProgress; } + } + + /// + /// 已经下载的总字节数 + /// + public ulong DownloadedBytes + { + get { return _downloadedBytes; } + } + + + internal DownloaderBase(BundleInfo bundleInfo) + { + _bundleInfo = bundleInfo; + } + internal void SendRequest(int failedTryAgain, int timeout) + { + if (string.IsNullOrEmpty(_bundleInfo.LocalPath)) + throw new System.ArgumentNullException(); + + if (_steps == ESteps.None) + { + _failedTryAgain = failedTryAgain; + _timeout = timeout; + _steps = ESteps.CreateDownload; + } + } + internal void SetDone() + { + _steps = ESteps.Succeed; + } + internal abstract void Update(); + + /// + /// 获取网络请求地址 + /// + protected string GetRequestURL() + { + // 轮流返回请求地址 + _requestCount++; + if (_requestCount % 2 == 0) + return _bundleInfo.RemoteFallbackURL; + else + return _bundleInfo.RemoteMainURL; + } + + /// + /// 获取资源包信息 + /// + public BundleInfo GetBundleInfo() + { + return _bundleInfo; + } + + /// + /// 检测下载器是否已经完成(无论成功或失败) + /// + public bool IsDone() + { + return _steps == ESteps.Succeed || _steps == ESteps.Failed; + } + + /// + /// 下载过程是否发生错误 + /// + public bool HasError() + { + return _steps == ESteps.Failed; + } + + /// + /// 报告错误信息 + /// + public void ReportError() + { + YooLogger.Error($"Failed to download : {_requestURL} Error : {_lastError}"); + } + + /// + /// 获取最近一条错误日志 + /// + public string GetLastError() + { + return _lastError; + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/DownloadSystem/DownloaderBase.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/DownloaderBase.cs.meta new file mode 100644 index 0000000..0a53087 --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/DownloaderBase.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dda4d1eafa2c9f34fade509f8dae9c04 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs b/Assets/YooAsset/Runtime/DownloadSystem/FileDownloader.cs similarity index 53% rename from Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs rename to Assets/YooAsset/Runtime/DownloadSystem/FileDownloader.cs index 3c29c5b..b23913a 100644 --- a/Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs +++ b/Assets/YooAsset/Runtime/DownloadSystem/FileDownloader.cs @@ -7,82 +7,39 @@ using UnityEngine.Networking; namespace YooAsset { - internal class FileDownloader + internal sealed class FileDownloader : DownloaderBase { - private enum ESteps - { - None, - CreateDownload, - CheckDownload, - TryAgain, - Succeed, - Failed, - } - - private readonly BundleInfo _bundleInfo; private UnityWebRequest _webRequest; private UnityWebRequestAsyncOperation _operationHandle; - private ESteps _steps = ESteps.None; - private string _lastError = string.Empty; - - private int _timeout; - private int _failedTryAgain; - private int _requestCount; - private string _requestURL; - // 重置变量 private bool _isAbort = false; private ulong _latestDownloadBytes; private float _latestDownloadRealtime; private float _tryAgainTimer; - /// - /// 下载进度(0-100f) - /// - public float DownloadProgress { private set; get; } - /// - /// 已经下载的总字节数 - /// - public ulong DownloadedBytes { private set; get; } - - - internal FileDownloader(BundleInfo bundleInfo) + internal FileDownloader(BundleInfo bundleInfo) : base(bundleInfo) { - _bundleInfo = bundleInfo; } - internal void SendRequest(int failedTryAgain, int timeout) - { - if (string.IsNullOrEmpty(_bundleInfo.LocalPath)) - throw new ArgumentNullException(); - - if (_steps == ESteps.None) - { - _failedTryAgain = failedTryAgain; - _timeout = timeout; - _steps = ESteps.CreateDownload; - } - } - internal void Update() + internal override void Update() { if (_steps == ESteps.None) return; - if (_steps == ESteps.Failed || _steps == ESteps.Succeed) + if (IsDone()) return; // 创建下载器 if (_steps == ESteps.CreateDownload) { // 重置变量 - DownloadProgress = 0f; - DownloadedBytes = 0; + _downloadProgress = 0f; + _downloadedBytes = 0; _isAbort = false; _latestDownloadBytes = 0; _latestDownloadRealtime = Time.realtimeSinceStartup; _tryAgainTimer = 0f; - _requestCount++; _requestURL = GetRequestURL(); _webRequest = new UnityWebRequest(_requestURL, UnityWebRequest.kHttpVerbGET); DownloadHandlerFile handler = new DownloadHandlerFile(_bundleInfo.LocalPath); @@ -96,8 +53,8 @@ namespace YooAsset // 检测下载结果 if (_steps == ESteps.CheckDownload) { - DownloadProgress = _webRequest.downloadProgress * 100f; - DownloadedBytes = _webRequest.downloadedBytes; + _downloadProgress = _webRequest.downloadProgress * 100f; + _downloadedBytes = _webRequest.downloadedBytes; if (_operationHandle.isDone == false) { CheckTimeout(); @@ -105,47 +62,50 @@ namespace YooAsset } // 检查网络错误 - bool isError = false; + bool hasError = false; #if UNITY_2020_3_OR_NEWER if (_webRequest.result != UnityWebRequest.Result.Success) { - isError = true; + hasError = true; _lastError = _webRequest.error; } #else if (_webRequest.isNetworkError || _webRequest.isHttpError) { - isError = true; + hasError = true; _lastError = _webRequest.error; } #endif // 检查文件完整性 - if (isError == false) + if (hasError == false) { // 注意:如果文件验证失败需要删除文件 if (DownloadSystem.CheckContentIntegrity(_bundleInfo) == false) { - isError = true; - _lastError = $"Verification failed"; - if (File.Exists(_bundleInfo.LocalPath)) - File.Delete(_bundleInfo.LocalPath); + hasError = true; + _lastError = $"Verification failed"; } } - if (isError) + if (hasError == false) + { + _steps = ESteps.Succeed; + DownloadSystem.CacheVerifyFile(_bundleInfo.Hash, _bundleInfo.BundleName); + } + else { ReportError(); + + if (File.Exists(_bundleInfo.LocalPath)) + File.Delete(_bundleInfo.LocalPath); + + // 失败后重新尝试 if (_failedTryAgain > 0) _steps = ESteps.TryAgain; else _steps = ESteps.Failed; } - else - { - _steps = ESteps.Succeed; - DownloadSystem.CacheVerifyFile(_bundleInfo.Hash, _bundleInfo.BundleName); - } // 释放下载器 DisposeWebRequest(); @@ -155,7 +115,7 @@ namespace YooAsset if (_steps == ESteps.TryAgain) { _tryAgainTimer += Time.unscaledDeltaTime; - if (_tryAgainTimer > 0.5f) + if (_tryAgainTimer > 1f) { _failedTryAgain--; _steps = ESteps.CreateDownload; @@ -163,19 +123,6 @@ namespace YooAsset } } } - internal void SetDone() - { - _steps = ESteps.Succeed; - } - - private string GetRequestURL() - { - // 轮流返回请求地址 - if (_requestCount % 2 == 0) - return _bundleInfo.RemoteFallbackURL; - else - return _bundleInfo.RemoteMainURL; - } private void CheckTimeout() { // 注意:在连续时间段内无新增下载数据及判定为超时 @@ -205,46 +152,5 @@ namespace YooAsset _operationHandle = null; } } - - /// - /// 获取资源包信息 - /// - public BundleInfo GetBundleInfo() - { - return _bundleInfo; - } - - /// - /// 检测下载器是否已经完成(无论成功或失败) - /// - public bool IsDone() - { - return _steps == ESteps.Succeed || _steps == ESteps.Failed; - } - - /// - /// 下载过程是否发生错误 - /// - /// - public bool HasError() - { - return _steps == ESteps.Failed; - } - - /// - /// 报告错误信息 - /// - public void ReportError() - { - YooLogger.Error($"Failed to download : {_requestURL} Error : {_lastError}"); - } - - /// - /// 获取最近一条错误日志 - /// - public string GetLastError() - { - return _lastError; - } } } \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/FileDownloader.cs.meta similarity index 100% rename from Assets/YooAsset/Runtime/PatchSystem/Download/FileDownloader.cs.meta rename to Assets/YooAsset/Runtime/DownloadSystem/FileDownloader.cs.meta diff --git a/Assets/YooAsset/Runtime/DownloadSystem/HttpDownloader.cs b/Assets/YooAsset/Runtime/DownloadSystem/HttpDownloader.cs new file mode 100644 index 0000000..4e71828 --- /dev/null +++ b/Assets/YooAsset/Runtime/DownloadSystem/HttpDownloader.cs @@ -0,0 +1,243 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Security; +using System.Threading; + +namespace YooAsset +{ + internal sealed class HttpDownloader : DownloaderBase + { + /// + /// 多线程下载器 + /// + private class ThreadDownloader + { + private const int BufferSize = 1042 * 4; + + private Thread _thread; + private string _url; + private string _savePath; + private string _fileHash; + private string _fileCRC; + private long _fileSize; + private int _timeout; + + /// + /// 下载是否结束 + /// + public bool IsDone = false; + + /// + /// 下载结果(成功或失败) + /// + public bool Result = false; + + /// + /// 错误日志 + /// + public string Error = string.Empty; + + /// + /// 下载进度 + /// + public float DownloadProgress = 0f; + + /// + /// 已经下载的总字节数 + /// + public ulong DownloadedBytes = 0; + + + /// + /// 开始下载 + /// + public void Run(string url, string savePath, string fileHash, string fileCRC, long fileSize, int timeout) + { + _url = url; + _savePath = savePath; + _fileHash = fileHash; + _fileCRC = fileCRC; + _fileSize = fileSize; + _timeout = timeout; + + _thread = new Thread(ThreadRun); + _thread.IsBackground = true; + _thread.Start(); + } + + /// + /// 销毁下载器 + /// + public void Dispose() + { + if (_thread != null) + { + _thread.Abort(); + _thread = null; + } + } + + + private void ThreadRun() + { + long fileTotalSize = _fileSize; + + FileStream fileStream = null; + Stream webStream = null; + HttpWebResponse fileResponse = null; + + try + { + // 创建文件流 + fileStream = new FileStream(_savePath, FileMode.OpenOrCreate, FileAccess.Write); + long fileLength = fileStream.Length; + + // 创建HTTP下载请求 + HttpWebRequest fileRequest = WebRequest.Create(_url) as HttpWebRequest; + fileRequest.Timeout = _timeout; + fileRequest.ReadWriteTimeout = _timeout; + fileRequest.ProtocolVersion = HttpVersion.Version10; + if (fileLength > 0) + { + // 注意:设置远端请求文件的起始位置 + fileRequest.AddRange(fileLength); + // 注意:设置本地文件流的起始位置 + fileStream.Seek(fileLength, SeekOrigin.Begin); + } + + // 读取下载数据并保存到文件 + fileResponse = fileRequest.GetResponse() as HttpWebResponse; + webStream = fileResponse.GetResponseStream(); + byte[] buffer = new byte[BufferSize]; + while (true) + { + int length = webStream.Read(buffer, 0, buffer.Length); + if (length <= 0) + break; + + fileStream.Write(buffer, 0, length); + + // 计算下载进度 + // 注意:原子操作保证数据安全 + fileLength += length; + float progress = fileLength / fileTotalSize; + DownloadProgress = progress; + DownloadedBytes = (ulong)fileLength; + } + + // 验证下载文件完整性 + bool verfiyResult = DownloadSystem.CheckContentIntegrity(_savePath, _fileSize, _fileCRC); + if (verfiyResult) + { + Result = true; + } + else + { + Result = false; + Error = $"Verify file content failed : {_fileHash}"; + } + } + catch (Exception e) + { + Result = false; + Error = e.Message; + } + finally + { + if (webStream != null) + { + webStream.Close(); + webStream.Dispose(); + } + + if (fileResponse != null) + { + fileResponse.Close(); + } + + if (fileStream != null) + { + fileStream.Close(); + fileStream.Dispose(); + } + + IsDone = true; + } + } + } + + + private ThreadDownloader _threadDownloader; + private float _tryAgainTimer; + + internal HttpDownloader(BundleInfo bundleInfo) : base(bundleInfo) + { + } + internal override void Update() + { + if (_steps == ESteps.None) + return; + if (IsDone()) + return; + + if (_steps == ESteps.CreateDownload) + { + // 重置变量 + _downloadProgress = 0f; + _downloadedBytes = 0; + _tryAgainTimer = 0f; + + _requestURL = GetRequestURL(); + _threadDownloader = new ThreadDownloader(); + _threadDownloader.Run(_requestURL, _bundleInfo.LocalPath, _bundleInfo.Hash, _bundleInfo.CRC, _bundleInfo.SizeBytes, _timeout); + _steps = ESteps.CheckDownload; + } + + if (_steps == ESteps.CheckDownload) + { + _downloadProgress = _threadDownloader.DownloadProgress * 100f; + _downloadedBytes = _threadDownloader.DownloadedBytes; + if (_threadDownloader.IsDone == false) + return; + + if (_threadDownloader.Result) + { + DownloadSystem.CacheVerifyFile(_bundleInfo.Hash, _bundleInfo.BundleName); + _steps = ESteps.Succeed; + } + else + { + _lastError = _threadDownloader.Error; + ReportError(); + + if (File.Exists(_bundleInfo.LocalPath)) + File.Delete(_bundleInfo.LocalPath); + + // 失败后重新尝试 + if (_failedTryAgain > 0) + _steps = ESteps.TryAgain; + else + _steps = ESteps.Failed; + } + + // 释放下载器 + _threadDownloader.Dispose(); + } + + // 重新尝试下载 + if (_steps == ESteps.TryAgain) + { + _tryAgainTimer += UnityEngine.Time.unscaledDeltaTime; + if (_tryAgainTimer > 1f) + { + _failedTryAgain--; + _steps = ESteps.CreateDownload; + YooLogger.Warning($"Try again download : {_requestURL}"); + } + } + } + } +} \ No newline at end of file diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/HttpDownloader.cs.meta similarity index 100% rename from Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs.meta rename to Assets/YooAsset/Runtime/DownloadSystem/HttpDownloader.cs.meta diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs b/Assets/YooAsset/Runtime/DownloadSystem/ThreadSyncContext.cs similarity index 100% rename from Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs rename to Assets/YooAsset/Runtime/DownloadSystem/ThreadSyncContext.cs diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/ThreadSyncContext.cs.meta similarity index 100% rename from Assets/YooAsset/Runtime/PatchSystem/Download/ThreadSyncContext.cs.meta rename to Assets/YooAsset/Runtime/DownloadSystem/ThreadSyncContext.cs.meta diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebDataRequester.cs b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebDataRequester.cs similarity index 100% rename from Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebDataRequester.cs rename to Assets/YooAsset/Runtime/DownloadSystem/UnityWebDataRequester.cs diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebDataRequester.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebDataRequester.cs.meta similarity index 100% rename from Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebDataRequester.cs.meta rename to Assets/YooAsset/Runtime/DownloadSystem/UnityWebDataRequester.cs.meta diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebFileRequester.cs b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebFileRequester.cs similarity index 100% rename from Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebFileRequester.cs rename to Assets/YooAsset/Runtime/DownloadSystem/UnityWebFileRequester.cs diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebFileRequester.cs.meta b/Assets/YooAsset/Runtime/DownloadSystem/UnityWebFileRequester.cs.meta similarity index 100% rename from Assets/YooAsset/Runtime/PatchSystem/Download/UnityWebFileRequester.cs.meta rename to Assets/YooAsset/Runtime/DownloadSystem/UnityWebFileRequester.cs.meta diff --git a/Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs b/Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs deleted file mode 100644 index a482dd2..0000000 --- a/Assets/YooAsset/Runtime/PatchSystem/Download/HttpDownloader.cs +++ /dev/null @@ -1,266 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Net.Security; -using System.Threading; - -namespace YooAsset -{ - internal class HttpDownloader - { - private enum ESteps - { - None, - CreateDownload, - CheckDownload, - Succeed, - Failed, - } - - private readonly BundleInfo _bundleInfo; - private ESteps _steps = ESteps.None; - - // 线程 - private bool _threadOver = false; - private bool _threadResult = false; - private string _threadError = string.Empty; - private Thread _thread; - - // 保留参数 - private int _timeout; - private int _failedTryAgain; - private int _requestCount; - - // 下载结果 - private string _downloadError = string.Empty; - private float _downloadProgress = 0f; - private long _downloadBytes = 0; - - /// - /// 下载进度(0-100f) - /// - public float DownloadProgress - { - get - { - return _downloadProgress; - } - } - - /// - /// 已经下载的总字节数 - /// - public long DownloadedBytes - { - get - { - return _downloadBytes; - } - } - - - internal HttpDownloader(BundleInfo bundleInfo) - { - _bundleInfo = bundleInfo; - } - internal void SendRequest(int failedTryAgain, int timeout) - { - _failedTryAgain = failedTryAgain; - _timeout = timeout; - } - internal void Update() - { - if (_steps == ESteps.None) - return; - if (_steps == ESteps.Succeed || _steps == ESteps.Failed) - return; - - if(_steps == ESteps.CreateDownload) - { - _downloadError = string.Empty; - _downloadProgress = 0f; - _downloadBytes = 0; - - _threadOver = false; - _threadResult = false; - _threadError = string.Empty; - _thread = new Thread(ThreadRun); - _thread.IsBackground = true; - _thread.Start(); - _steps = ESteps.CheckDownload; - } - - if(_steps == ESteps.CheckDownload) - { - if (_threadOver == false) - return; - - if(_thread != null) - { - _thread.Abort(); - _thread = null; - } - - _downloadError = _threadError; - if (_threadResult) - { - DownloadSystem.CacheVerifyFile(_bundleInfo.Hash, _bundleInfo.BundleName); - _steps = ESteps.Succeed; - } - else - { - // 失败后重新尝试 - if(_failedTryAgain > 0) - { - _failedTryAgain--; - _steps = ESteps.CreateDownload; - } - else - { - _steps = ESteps.Failed; - } - } - } - } - internal void SetDone() - { - _steps = ESteps.Succeed; - } - - /// - /// 获取资源包信息 - /// - public BundleInfo GetBundleInfo() - { - return _bundleInfo; - } - - /// - /// 检测下载器是否已经完成(无论成功或失败) - /// - public bool IsDone() - { - return _steps == ESteps.Succeed || _steps == ESteps.Failed; - } - - /// - /// 下载过程是否发生错误 - /// - public bool HasError() - { - return _steps == ESteps.Failed; - } - - /// - /// 报告错误信息 - /// - public void ReportError() - { - YooLogger.Error(_downloadError); - } - - - #region 多线程下载 - public const int BufferSize = 1042 * 4; - private void ThreadRun() - { - string url = GetRequestURL(); - string savePath = _bundleInfo.LocalPath; - long fileTotalSize = _bundleInfo.SizeBytes; - - FileStream fileStream = null; - Stream webStream = null; - HttpWebResponse fileResponse = null; - - try - { - // 创建文件流 - fileStream = new FileStream(savePath, FileMode.OpenOrCreate, FileAccess.Write); - long fileLength = fileStream.Length; - - // 创建HTTP下载请求 - HttpWebRequest fileRequest = WebRequest.Create(url) as HttpWebRequest; - fileRequest.Timeout = _timeout; - fileRequest.ReadWriteTimeout = _timeout; - fileRequest.ProtocolVersion = HttpVersion.Version10; - if (fileLength > 0) - { - // 注意:设置远端请求文件的起始位置 - fileRequest.AddRange(fileLength); - // 注意:设置本地文件流的起始位置 - fileStream.Seek(fileLength, SeekOrigin.Begin); - } - - // 读取下载数据并保存到文件 - fileResponse = fileRequest.GetResponse() as HttpWebResponse; - webStream = fileResponse.GetResponseStream(); - byte[] buffer = new byte[BufferSize]; - while (true) - { - int length = webStream.Read(buffer, 0, buffer.Length); - if (length <= 0) - break; - - fileStream.Write(buffer, 0, length); - - // 计算下载进度 - // 注意:原子操作保证数据安全 - fileLength += length; - float progress = (fileLength / fileTotalSize) * 100f; - _downloadProgress = progress; - _downloadBytes = fileLength; - } - - // 验证下载文件完整性 - bool verfiyResult = DownloadSystem.CheckContentIntegrity(savePath, _bundleInfo.SizeBytes, _bundleInfo.CRC); - if(verfiyResult) - { - _threadResult = true; - } - else - { - _threadResult = false; - _threadError = $"Verify file content failed : {_bundleInfo.Hash}"; - } - } - catch (Exception e) - { - _threadResult = false; - _threadError = e.Message; - } - finally - { - if (webStream != null) - { - webStream.Close(); - webStream.Dispose(); - } - - if (fileResponse != null) - { - fileResponse.Close(); - } - - if (fileStream != null) - { - fileStream.Close(); - fileStream.Dispose(); - } - - _threadOver = true; - } - } - private string GetRequestURL() - { - // 轮流返回请求地址 - _requestCount++; - if (_requestCount % 2 == 0) - return _bundleInfo.RemoteFallbackURL; - else - return _bundleInfo.RemoteMainURL; - } - #endregion - } -} \ No newline at end of file