更新demo

更新demo
This commit is contained in:
ALEXTANG
2024-03-18 15:35:15 +08:00
parent 17a5d7425c
commit a2255b80cd
1028 changed files with 55353 additions and 35278 deletions

View File

@@ -0,0 +1,41 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// 自定义下载器的请求委托
/// </summary>
public delegate UnityWebRequest DownloadRequestDelegate(string url);
internal static class DownloadHelper
{
/// <summary>
/// 下载失败后清理文件的HTTP错误码
/// </summary>
public static List<long> ClearFileResponseCodes { set; get; }
/// <summary>
/// 自定义下载器的请求委托
/// </summary>
public static DownloadRequestDelegate RequestDelegate = null;
/// <summary>
/// 创建一个新的网络请求
/// </summary>
public static UnityWebRequest NewRequest(string requestURL)
{
UnityWebRequest webRequest;
if (RequestDelegate != null)
webRequest = RequestDelegate.Invoke(requestURL);
else
webRequest = new UnityWebRequest(requestURL, UnityWebRequest.kHttpVerbGET);
return webRequest;
}
}
}

View File

@@ -0,0 +1,150 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// 1. 保证每一时刻资源文件只存在一个下载器
/// 2. 保证下载器下载完成后立刻验证并缓存
/// 3. 保证资源文件不会被重复下载
/// </summary>
internal class DownloadManager
{
private readonly Dictionary<string, DownloaderBase> _downloaders = new Dictionary<string, DownloaderBase>(1000);
private readonly List<string> _removeList = new List<string>(1000);
private uint _breakpointResumeFileSize;
/// <summary>
/// 所属包裹
/// </summary>
public readonly string PackageName;
public DownloadManager(string packageName)
{
PackageName = packageName;
}
/// <summary>
/// 初始化
/// </summary>
public void Initialize(uint breakpointResumeFileSize)
{
_breakpointResumeFileSize = breakpointResumeFileSize;
}
/// <summary>
/// 更新下载器
/// </summary>
public void Update()
{
// 更新下载器
_removeList.Clear();
foreach (var valuePair in _downloaders)
{
var downloader = valuePair.Value;
downloader.Update();
if (downloader.IsDone())
{
_removeList.Add(valuePair.Key);
}
}
// 移除下载器
foreach (var key in _removeList)
{
_downloaders.Remove(key);
}
}
/// <summary>
/// 销毁所有下载器
/// </summary>
public void DestroyAll()
{
foreach (var valuePair in _downloaders)
{
var downloader = valuePair.Value;
downloader.Abort();
}
_downloaders.Clear();
_removeList.Clear();
}
/// <summary>
/// 创建下载器
/// 注意:只有第一次请求的参数才有效
/// </summary>
public DownloaderBase CreateDownload(BundleInfo bundleInfo, int failedTryAgain, int timeout = 60)
{
// 查询存在的下载器
if (_downloaders.TryGetValue(bundleInfo.CachedDataFilePath, out var downloader))
{
downloader.Reference();
return downloader;
}
// 如果资源已经缓存
if (bundleInfo.IsCached())
{
var completedDownloader = new CompletedDownloader(bundleInfo);
return completedDownloader;
}
// 创建新的下载器
DownloaderBase newDownloader = null;
YooLogger.Log($"Beginning to download bundle : {bundleInfo.Bundle.BundleName} URL : {bundleInfo.RemoteMainURL}");
#if UNITY_WEBGL
if (bundleInfo.Bundle.Buildpipeline == EDefaultBuildPipeline.RawFileBuildPipeline.ToString())
{
FileUtility.CreateFileDirectory(bundleInfo.CachedDataFilePath);
System.Type requesterType = typeof(FileGeneralRequest);
newDownloader = new FileDownloader(bundleInfo, requesterType, failedTryAgain, timeout);
}
else
{
System.Type requesterType = typeof(AssetBundleWebRequest);
newDownloader = new WebDownloader(bundleInfo, requesterType, failedTryAgain, timeout);
}
#else
FileUtility.CreateFileDirectory(bundleInfo.CachedDataFilePath);
bool resumeDownload = bundleInfo.Bundle.FileSize >= _breakpointResumeFileSize;
if (resumeDownload)
{
System.Type requesterType = typeof(FileResumeRequest);
newDownloader = new FileDownloader(bundleInfo, requesterType, failedTryAgain, timeout);
}
else
{
System.Type requesterType = typeof(FileGeneralRequest);
newDownloader = new FileDownloader(bundleInfo, requesterType, failedTryAgain, timeout);
}
#endif
// 返回新创建的下载器
_downloaders.Add(bundleInfo.CachedDataFilePath, newDownloader);
newDownloader.Reference();
return newDownloader;
}
/// <summary>
/// 停止不再使用的下载器
/// </summary>
public void AbortUnusedDownloader()
{
foreach (var valuePair in _downloaders)
{
var downloader = valuePair.Value;
if (downloader.RefCount <= 0)
{
downloader.Abort();
}
}
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: a439c9a8f36dcc942b92a8e8af927237
guid: 6fbbbfe01e018b241bf33469f870a4b8
MonoImporter:
externalObjects: {}
serializedVersion: 2

View File

@@ -1,30 +0,0 @@

namespace YooAsset
{
public struct DownloadReport
{
/// <summary>
/// 下载进度0f~1f
/// </summary>
public float Progress;
/// <summary>
/// 需要下载的总字节数
/// </summary>
public ulong TotalSize;
/// <summary>
/// 已经下载的字节数
/// </summary>
public ulong DownloadedBytes;
public static DownloadReport CreateDefaultReport()
{
DownloadReport report = new DownloadReport();
report.Progress = 0f;
report.TotalSize = 0;
report.DownloadedBytes = 0;
return report;
}
}
}

View File

@@ -0,0 +1,36 @@

namespace YooAsset
{
public struct DownloadStatus
{
/// <summary>
/// 下载是否完成
/// </summary>
public bool IsDone;
/// <summary>
/// 下载进度0f~1f
/// </summary>
public float Progress;
/// <summary>
/// 需要下载的总字节数
/// </summary>
public ulong TotalBytes;
/// <summary>
/// 已经下载的字节数
/// </summary>
public ulong DownloadedBytes;
public static DownloadStatus CreateDefaultStatus()
{
DownloadStatus status = new DownloadStatus();
status.IsDone = false;
status.Progress = 0f;
status.TotalBytes = 0;
status.DownloadedBytes = 0;
return status;
}
}
}

View File

@@ -1,184 +0,0 @@
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// 自定义下载器的请求委托
/// </summary>
public delegate UnityWebRequest DownloadRequestDelegate(string url);
/// <summary>
/// 1. 保证每一时刻资源文件只存在一个下载器
/// 2. 保证下载器下载完成后立刻验证并缓存
/// 3. 保证资源文件不会被重复下载
/// </summary>
internal static class DownloadSystem
{
private static readonly Dictionary<string, DownloaderBase> _downloaderDic = new Dictionary<string, DownloaderBase>();
private static readonly List<string> _removeList = new List<string>(100);
/// <summary>
/// 自定义下载器的请求委托
/// </summary>
public static DownloadRequestDelegate RequestDelegate = null;
/// <summary>
/// 自定义的证书认证实例
/// </summary>
public static CertificateHandler CertificateHandlerInstance = null;
/// <summary>
/// 网络重定向次数
/// </summary>
public static int RedirectLimit { set; get; } = -1;
/// <summary>
/// 启用断点续传功能文件的最小字节数
/// </summary>
public static int BreakpointResumeFileSize { set; get; } = int.MaxValue;
/// <summary>
/// 下载失败后清理文件的HTTP错误码
/// </summary>
public static List<long> ClearFileResponseCodes { set; get; }
/// <summary>
/// 初始化下载器
/// </summary>
public static void Initialize()
{
}
/// <summary>
/// 更新下载器
/// </summary>
public static void Update()
{
// 更新下载器
_removeList.Clear();
foreach (var valuePair in _downloaderDic)
{
var downloader = valuePair.Value;
downloader.Update();
if (downloader.IsDone())
_removeList.Add(valuePair.Key);
}
// 移除下载器
foreach (var key in _removeList)
{
_downloaderDic.Remove(key);
}
}
/// <summary>
/// 销毁所有下载器
/// </summary>
public static void DestroyAll()
{
foreach (var valuePair in _downloaderDic)
{
var downloader = valuePair.Value;
downloader.Abort();
}
_downloaderDic.Clear();
_removeList.Clear();
RequestDelegate = null;
CertificateHandlerInstance = null;
BreakpointResumeFileSize = int.MaxValue;
ClearFileResponseCodes = null;
}
/// <summary>
/// 创建下载器
/// 注意:只有第一次请求的参数才有效
/// </summary>
public static DownloaderBase CreateDownload(BundleInfo bundleInfo, int failedTryAgain, int timeout = 60)
{
// 查询存在的下载器
if (_downloaderDic.TryGetValue(bundleInfo.Bundle.CachedDataFilePath, out var downloader))
return downloader;
// 如果资源已经缓存
if (CacheSystem.IsCached(bundleInfo.Bundle.PackageName, bundleInfo.Bundle.CacheGUID))
{
var completedDownloader = new CompletedDownloader(bundleInfo);
return completedDownloader;
}
// 创建新的下载器
YooLogger.Log($"Beginning to download bundle : {bundleInfo.Bundle.BundleName} URL : {bundleInfo.RemoteMainURL}");
#if UNITY_WEBGL
if (bundleInfo.Bundle.IsRawFile)
{
FileUtility.CreateFileDirectory(bundleInfo.Bundle.CachedDataFilePath);
DownloaderBase newDownloader = new FileGeneralDownloader(bundleInfo, failedTryAgain, timeout);
_downloaderDic.Add(bundleInfo.Bundle.CachedDataFilePath, newDownloader);
return newDownloader;
}
else
{
WebDownloader newDownloader = new WebDownloader(bundleInfo, failedTryAgain, timeout);
_downloaderDic.Add(bundleInfo.Bundle.CachedDataFilePath, newDownloader);
return newDownloader;
}
#else
FileUtility.CreateFileDirectory(bundleInfo.Bundle.CachedDataFilePath);
bool resumeDownload = bundleInfo.Bundle.FileSize >= BreakpointResumeFileSize;
DownloaderBase newDownloader;
if (resumeDownload)
newDownloader = new FileResumeDownloader(bundleInfo, failedTryAgain, timeout);
else
newDownloader = new FileGeneralDownloader(bundleInfo, failedTryAgain, timeout);
_downloaderDic.Add(bundleInfo.Bundle.CachedDataFilePath, newDownloader);
return newDownloader;
#endif
}
/// <summary>
/// 创建一个新的网络请求
/// </summary>
public static UnityWebRequest NewRequest(string requestURL)
{
UnityWebRequest webRequest;
if (RequestDelegate != null)
webRequest = RequestDelegate.Invoke(requestURL);
else
webRequest = new UnityWebRequest(requestURL, UnityWebRequest.kHttpVerbGET);
SetUnityWebRequestParam(webRequest);
return webRequest;
}
/// <summary>
/// 设置网络请求的自定义参数
/// </summary>
private static void SetUnityWebRequestParam(UnityWebRequest webRequest)
{
if (RedirectLimit >= 0)
webRequest.redirectLimit = RedirectLimit;
if (CertificateHandlerInstance != null)
{
webRequest.certificateHandler = CertificateHandlerInstance;
webRequest.disposeCertificateHandlerOnDispose = false;
}
}
/// <summary>
/// 获取下载器的总数
/// </summary>
public static int GetDownloaderTotalCount()
{
return _downloaderDic.Count;
}
}
}

View File

@@ -1,23 +1,28 @@

using UnityEngine;
namespace YooAsset
{
internal sealed class CompletedDownloader : DownloaderBase
{
public CompletedDownloader(BundleInfo bundleInfo) : base(bundleInfo, 0, 0)
{
_downloadProgress = 1f;
_downloadedBytes = (ulong)bundleInfo.Bundle.FileSize;
_status = EStatus.Succeed;
}
internal sealed class CompletedDownloader : DownloaderBase
{
public CompletedDownloader(BundleInfo bundleInfo) : base(bundleInfo, null, 0, 0)
{
DownloadProgress = 1f;
DownloadedBytes = (ulong)bundleInfo.Bundle.FileSize;
_status = EStatus.Succeed;
}
public override void SendRequest(params object[] param)
{
}
public override void Update()
{
}
public override void Abort()
{
}
}
public override void SendRequest(params object[] param)
{
}
public override void Update()
{
}
public override void Abort()
{
}
public override AssetBundle GetAssetBundle()
{
throw new System.NotImplementedException();
}
}
}

View File

@@ -1,87 +0,0 @@
using System;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// 支持Unity2018版本的断点续传下载器
/// </summary>
internal class DownloadHandlerFileRange : DownloadHandlerScript
{
private string _fileSavePath;
private long _fileTotalSize;
private UnityWebRequest _webRequest;
private FileStream _fileStream;
private long _localFileSize = 0;
private long _curFileSize = 0;
public DownloadHandlerFileRange(string fileSavePath, long fileTotalSize, UnityWebRequest webRequest) : base(new byte[1024 * 1024])
{
_fileSavePath = fileSavePath;
_fileTotalSize = fileTotalSize;
_webRequest = webRequest;
if (File.Exists(fileSavePath))
{
FileInfo fileInfo = new FileInfo(fileSavePath);
_localFileSize = fileInfo.Length;
}
_fileStream = new FileStream(_fileSavePath, FileMode.Append, FileAccess.Write);
_curFileSize = _localFileSize;
}
protected override bool ReceiveData(byte[] data, int dataLength)
{
if (data == null || dataLength == 0 || _webRequest.responseCode >= 400)
return false;
if (_fileStream == null)
return false;
_fileStream.Write(data, 0, dataLength);
_curFileSize += dataLength;
return true;
}
/// <summary>
/// UnityWebRequest.downloadHandler.data
/// </summary>
protected override byte[] GetData()
{
return null;
}
/// <summary>
/// UnityWebRequest.downloadHandler.text
/// </summary>
protected override string GetText()
{
return null;
}
/// <summary>
/// UnityWebRequest.downloadProgress
/// </summary>
protected override float GetProgress()
{
return _fileTotalSize == 0 ? 0 : ((float)_curFileSize) / _fileTotalSize;
}
/// <summary>
/// 释放下载句柄
/// </summary>
public void Cleanup()
{
if (_fileStream != null)
{
_fileStream.Flush();
_fileStream.Dispose();
_fileStream = null;
}
}
}
}

View File

@@ -1,193 +1,182 @@
using System.IO;
using System;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal abstract class DownloaderBase
{
public enum EStatus
{
None = 0,
Succeed,
Failed
}
internal abstract class DownloaderBase
{
public enum EStatus
{
None = 0,
Succeed,
Failed
}
protected readonly BundleInfo _bundleInfo;
protected readonly int _timeout;
protected int _failedTryAgain;
protected readonly BundleInfo _bundleInfo;
protected readonly System.Type _requesterType;
protected readonly int _timeout;
protected int _failedTryAgain;
protected UnityWebRequest _webRequest;
protected EStatus _status = EStatus.None;
protected string _lastError = string.Empty;
protected long _lastCode = 0;
protected IWebRequester _requester;
protected EStatus _status = EStatus.None;
protected string _lastestNetError = string.Empty;
protected long _lastestHttpCode = 0;
// 请求次数
protected int _requestCount = 0;
protected string _requestURL;
// 请求次数
protected int _requestCount = 0;
protected string _requestURL;
// 下载进度
protected float _downloadProgress = 0f;
protected ulong _downloadedBytes = 0;
// 超时相关
protected bool _isAbort = false;
protected ulong _latestDownloadBytes;
protected float _latestDownloadRealtime;
protected float _tryAgainTimer;
// 超时相关
protected bool _isAbort = false;
protected ulong _latestDownloadBytes;
protected float _latestDownloadRealtime;
protected float _tryAgainTimer;
/// <summary>
/// 是否等待异步结束
/// 警告只能用于解压APP内部资源
/// </summary>
public bool WaitForAsyncComplete = false;
/// <summary>
/// 是否等待异步结束
/// 警告只能用于解压APP内部资源
/// </summary>
public bool WaitForAsyncComplete = false;
/// <summary>
/// 下载进度0f~1f
/// </summary>
public float DownloadProgress { protected set; get; }
/// <summary>
/// 下载进度0f~1f
/// </summary>
public float DownloadProgress
{
get { return _downloadProgress; }
}
/// <summary>
/// 已经下载的总字节数
/// </summary>
public ulong DownloadedBytes { protected set; get; }
/// <summary>
/// 已经下载的总字节
/// </summary>
public ulong DownloadedBytes
{
get { return _downloadedBytes; }
}
/// <summary>
/// 引用计
/// </summary>
public int RefCount { private set; get; }
public DownloaderBase(BundleInfo bundleInfo, int failedTryAgain, int timeout)
{
_bundleInfo = bundleInfo;
_failedTryAgain = failedTryAgain;
_timeout = timeout;
}
public abstract void SendRequest(params object[] param);
public abstract void Update();
public abstract void Abort();
public DownloaderBase(BundleInfo bundleInfo, System.Type requesterType, int failedTryAgain, int timeout)
{
_bundleInfo = bundleInfo;
_requesterType = requesterType;
_failedTryAgain = failedTryAgain;
_timeout = timeout;
}
public abstract void SendRequest(params object[] args);
public abstract void Update();
public abstract void Abort();
public abstract AssetBundle GetAssetBundle();
/// <summary>
/// 获取下载文件的大小
/// </summary>
/// <returns></returns>
public long GetDownloadFileSize()
{
return _bundleInfo.Bundle.FileSize;
}
/// <summary>
/// 引用(引用计数递加)
/// </summary>
public void Reference()
{
RefCount++;
}
/// <summary>
/// 获取下载文件的资源包名
/// </summary>
public string GetDownloadBundleName()
{
return _bundleInfo.Bundle.BundleName;
}
/// <summary>
/// 释放(引用计数递减)
/// </summary>
public void Release()
{
RefCount--;
}
/// <summary>
/// 检测下载器是否已经完成(无论成功或失败)
/// </summary>
public bool IsDone()
{
return _status == EStatus.Succeed || _status == EStatus.Failed;
}
/// <summary>
/// 检测下载器是否已经完成(无论成功或失败)
/// </summary>
public bool IsDone()
{
return _status == EStatus.Succeed || _status == EStatus.Failed;
}
/// <summary>
/// 下载过程是否发生错误
/// </summary>
public bool HasError()
{
return _status == EStatus.Failed;
}
/// <summary>
/// 下载过程是否发生错误
/// </summary>
public bool HasError()
{
return _status == EStatus.Failed;
}
/// <summary>
/// 按照错误级别打印错误
/// </summary>
public void ReportError()
{
YooLogger.Error(GetLastError());
}
/// <summary>
/// 按照错误级别打印错误
/// </summary>
public void ReportError()
{
YooLogger.Error(GetLastError());
}
/// <summary>
/// 按照警告级别打印错误
/// </summary>
public void ReportWarning()
{
YooLogger.Warning(GetLastError());
}
/// <summary>
/// 按照警告级别打印错误
/// </summary>
public void ReportWarning()
{
YooLogger.Warning(GetLastError());
}
/// <summary>
/// 获取最近发生的错误信息
/// </summary>
public string GetLastError()
{
return $"Failed to download : {_requestURL} Error : {_lastError} Code : {_lastCode}";
}
/// <summary>
/// 获取最近发生的错误信息
/// </summary>
public string GetLastError()
{
return $"Failed to download : {_requestURL} Error : {_lastestNetError} Code : {_lastestHttpCode}";
}
/// <summary>
/// 获取下载文件的大小
/// </summary>
/// <returns></returns>
public long GetDownloadFileSize()
{
return _bundleInfo.Bundle.FileSize;
}
/// <summary>
/// 获取下载的资源包名称
/// </summary>
public string GetDownloadBundleName()
{
return _bundleInfo.Bundle.BundleName;
}
/// <summary>
/// 获取网络请求地址
/// </summary>
protected string GetRequestURL()
{
// 轮流返回请求地址
_requestCount++;
if (_requestCount % 2 == 0)
return _bundleInfo.RemoteFallbackURL;
else
return _bundleInfo.RemoteMainURL;
}
/// <summary>
/// 获取网络请求地址
/// </summary>
protected string GetRequestURL()
{
// 轮流返回请求地址
_requestCount++;
if (_requestCount % 2 == 0)
return _bundleInfo.RemoteFallbackURL;
else
return _bundleInfo.RemoteMainURL;
}
/// <summary>
/// 超时判定方法
/// </summary>
protected void CheckTimeout()
{
// 注意:在连续时间段内无新增下载数据及判定为超时
if (_isAbort == false)
{
if (_latestDownloadBytes != DownloadedBytes)
{
_latestDownloadBytes = DownloadedBytes;
_latestDownloadRealtime = Time.realtimeSinceStartup;
}
/// <summary>
/// 超时判定方法
/// </summary>
protected void CheckTimeout()
{
// 注意:在连续时间段内无新增下载数据及判定为超时
if (_isAbort == false)
{
if (_latestDownloadBytes != DownloadedBytes)
{
_latestDownloadBytes = DownloadedBytes;
_latestDownloadRealtime = Time.realtimeSinceStartup;
}
float offset = Time.realtimeSinceStartup - _latestDownloadRealtime;
if (offset > _timeout)
{
YooLogger.Warning($"Web file request timeout : {_requestURL}");
_webRequest.Abort();
_isAbort = true;
}
}
}
/// <summary>
/// 缓存下载文件
/// </summary>
protected void CachingFile(string tempFilePath)
{
string infoFilePath = _bundleInfo.Bundle.CachedInfoFilePath;
string dataFilePath = _bundleInfo.Bundle.CachedDataFilePath;
string dataFileCRC = _bundleInfo.Bundle.FileCRC;
long dataFileSize = _bundleInfo.Bundle.FileSize;
if (File.Exists(infoFilePath))
File.Delete(infoFilePath);
if (File.Exists(dataFilePath))
File.Delete(dataFilePath);
FileInfo fileInfo = new FileInfo(tempFilePath);
fileInfo.MoveTo(dataFilePath);
// 写入信息文件记录验证数据
CacheFileInfo.WriteInfoToFile(infoFilePath, dataFileCRC, dataFileSize);
// 记录缓存文件
var wrapper = new PackageCache.RecordWrapper(infoFilePath, dataFilePath, dataFileCRC, dataFileSize);
CacheSystem.RecordFile(_bundleInfo.Bundle.PackageName, _bundleInfo.Bundle.CacheGUID, wrapper);
}
}
float offset = Time.realtimeSinceStartup - _latestDownloadRealtime;
if (offset > _timeout)
{
YooLogger.Warning($"Web file request timeout : {_requestURL}");
if (_requester != null)
_requester.Abort();
_isAbort = true;
}
}
}
}
}

View File

@@ -0,0 +1,216 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
namespace YooAsset
{
/// <summary>
/// 文件下载器
/// </summary>
internal sealed class FileDownloader : DownloaderBase
{
private enum ESteps
{
None,
PrepareDownload,
CreateDownloader,
CheckDownload,
VerifyTempFile,
WaitingVerifyTempFile,
CachingFile,
TryAgain,
Done,
}
private VerifyTempFileOperation _verifyFileOp = null;
private ESteps _steps = ESteps.None;
public FileDownloader(BundleInfo bundleInfo, System.Type requesterType, int failedTryAgain, int timeout) : base(bundleInfo, requesterType, failedTryAgain, timeout)
{
}
public override void SendRequest(params object[] args)
{
if (_steps == ESteps.None)
{
_steps = ESteps.PrepareDownload;
}
}
public override void Update()
{
if (_steps == ESteps.None)
return;
if (IsDone())
return;
// 准备下载
if (_steps == ESteps.PrepareDownload)
{
// 获取请求地址
_requestURL = GetRequestURL();
// 重置变量
DownloadProgress = 0f;
DownloadedBytes = 0;
// 重置变量
_isAbort = false;
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
// 重置计时器
if (_tryAgainTimer > 0f)
YooLogger.Warning($"Try again download : {_requestURL}");
_tryAgainTimer = 0f;
_steps = ESteps.CreateDownloader;
}
// 创建下载器
if (_steps == ESteps.CreateDownloader)
{
_requester = (IWebRequester)Activator.CreateInstance(_requesterType);
_requester.Create(_requestURL, _bundleInfo);
_steps = ESteps.CheckDownload;
}
// 检测下载结果
if (_steps == ESteps.CheckDownload)
{
_requester.Update();
DownloadedBytes = _requester.DownloadedBytes;
DownloadProgress = _requester.DownloadProgress;
if (_requester.IsDone() == false)
{
CheckTimeout();
return;
}
_lastestNetError = _requester.RequestNetError;
_lastestHttpCode = _requester.RequestHttpCode;
if (_requester.Status != ERequestStatus.Success)
{
_steps = ESteps.TryAgain;
}
else
{
_steps = ESteps.VerifyTempFile;
}
}
// 验证下载文件
if (_steps == ESteps.VerifyTempFile)
{
VerifyTempFileElement element = new VerifyTempFileElement(_bundleInfo.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize);
_verifyFileOp = VerifyTempFileOperation.CreateOperation(element);
OperationSystem.StartOperation(_bundleInfo.Bundle.PackageName, _verifyFileOp);
_steps = ESteps.WaitingVerifyTempFile;
}
// 等待验证完成
if (_steps == ESteps.WaitingVerifyTempFile)
{
if (WaitForAsyncComplete)
_verifyFileOp.InternalOnUpdate();
if (_verifyFileOp.IsDone == false)
return;
if (_verifyFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CachingFile;
}
else
{
string tempFilePath = _bundleInfo.TempDataFilePath;
if (File.Exists(tempFilePath))
File.Delete(tempFilePath);
_lastestNetError = _verifyFileOp.Error;
_steps = ESteps.TryAgain;
}
}
// 缓存下载文件
if (_steps == ESteps.CachingFile)
{
try
{
CachingFile();
_status = EStatus.Succeed;
_steps = ESteps.Done;
}
catch (Exception e)
{
_lastestNetError = e.Message;
_steps = ESteps.TryAgain;
}
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (_failedTryAgain <= 0)
{
ReportError();
_status = EStatus.Failed;
_steps = ESteps.Done;
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
_failedTryAgain--;
_steps = ESteps.PrepareDownload;
ReportWarning();
}
}
}
public override void Abort()
{
if (_requester != null)
_requester.Abort();
if (IsDone() == false)
{
_status = EStatus.Failed;
_steps = ESteps.Done;
_lastestNetError = "user abort";
_lastestHttpCode = 0;
}
}
public override AssetBundle GetAssetBundle()
{
throw new NotImplementedException();
}
/// <summary>
/// 缓存下载文件
/// </summary>
private void CachingFile()
{
string tempFilePath = _bundleInfo.TempDataFilePath;
string infoFilePath = _bundleInfo.CachedInfoFilePath;
string dataFilePath = _bundleInfo.CachedDataFilePath;
string dataFileCRC = _bundleInfo.Bundle.FileCRC;
long dataFileSize = _bundleInfo.Bundle.FileSize;
if (File.Exists(infoFilePath))
File.Delete(infoFilePath);
if (File.Exists(dataFilePath))
File.Delete(dataFilePath);
// 移动临时文件路径
FileInfo fileInfo = new FileInfo(tempFilePath);
fileInfo.MoveTo(dataFilePath);
// 写入信息文件记录验证数据
CacheHelper.WriteInfoToFile(infoFilePath, dataFileCRC, dataFileSize);
// 记录缓存文件
_bundleInfo.CacheRecord();
}
}
}

View File

@@ -1,229 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// 普通的下载器
/// </summary>
internal sealed class FileGeneralDownloader : DownloaderBase
{
private enum ESteps
{
None,
PrepareDownload,
CreateDownloader,
CheckDownload,
VerifyTempFile,
WaitingVerifyTempFile,
CachingFile,
TryAgain,
Done,
}
private readonly string _tempFilePath;
private VerifyTempFileOperation _verifyFileOp = null;
private ESteps _steps = ESteps.None;
public FileGeneralDownloader(BundleInfo bundleInfo, int failedTryAgain, int timeout) : base(bundleInfo, failedTryAgain, timeout)
{
_tempFilePath = bundleInfo.Bundle.TempDataFilePath;
}
public override void SendRequest(params object[] param)
{
if (_steps == ESteps.None)
{
_steps = ESteps.PrepareDownload;
}
}
public override void Update()
{
if (_steps == ESteps.None)
return;
if (IsDone())
return;
// 准备下载
if (_steps == ESteps.PrepareDownload)
{
// 获取请求地址
_requestURL = GetRequestURL();
// 重置变量
_downloadProgress = 0f;
_downloadedBytes = 0;
// 重置变量
_isAbort = false;
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
// 重置计时器
if(_tryAgainTimer > 0f)
YooLogger.Warning($"Try again download : {_requestURL}");
_tryAgainTimer = 0f;
// 删除临时文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
_steps = ESteps.CreateDownloader;
}
// 创建下载器
if (_steps == ESteps.CreateDownloader)
{
_webRequest = DownloadSystem.NewRequest(_requestURL);
DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath);
handler.removeFileOnAbort = true;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
_steps = ESteps.CheckDownload;
}
// 检测下载结果
if (_steps == ESteps.CheckDownload)
{
_downloadProgress = _webRequest.downloadProgress;
_downloadedBytes = _webRequest.downloadedBytes;
if (_webRequest.isDone == false)
{
CheckTimeout();
return;
}
bool hasError = false;
// 检查网络错误
#if UNITY_2020_3_OR_NEWER
if (_webRequest.result != UnityWebRequest.Result.Success)
{
hasError = true;
_lastError = _webRequest.error;
_lastCode = _webRequest.responseCode;
}
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
hasError = true;
_lastError = _webRequest.error;
_lastCode = _webRequest.responseCode;
}
#endif
// 如果网络异常
if (hasError)
{
// 下载失败之后删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
_steps = ESteps.TryAgain;
}
else
{
_steps = ESteps.VerifyTempFile;
}
// 最终释放下载器
DisposeWebRequest();
}
// 验证下载文件
if (_steps == ESteps.VerifyTempFile)
{
VerifyTempFileElement element = new VerifyTempFileElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize);
_verifyFileOp = VerifyTempFileOperation.CreateOperation(element);
OperationSystem.StartOperation(_verifyFileOp);
_steps = ESteps.WaitingVerifyTempFile;
}
// 等待验证完成
if (_steps == ESteps.WaitingVerifyTempFile)
{
if (WaitForAsyncComplete)
_verifyFileOp.Update();
if (_verifyFileOp.IsDone == false)
return;
if (_verifyFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CachingFile;
}
else
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
_lastError = _verifyFileOp.Error;
_steps = ESteps.TryAgain;
}
}
// 缓存下载文件
if (_steps == ESteps.CachingFile)
{
try
{
CachingFile(_tempFilePath);
_status = EStatus.Succeed;
_steps = ESteps.Done;
_lastError = string.Empty;
_lastCode = 0;
}
catch (Exception e)
{
_lastError = e.Message;
_steps = ESteps.TryAgain;
}
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (_failedTryAgain <= 0)
{
ReportError();
_status = EStatus.Failed;
_steps = ESteps.Done;
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
_failedTryAgain--;
_steps = ESteps.PrepareDownload;
ReportWarning();
}
}
}
public override void Abort()
{
if (IsDone() == false)
{
_status = EStatus.Failed;
_steps = ESteps.Done;
_lastError = "user abort";
_lastCode = 0;
DisposeWebRequest();
}
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
}
}
}
}

View File

@@ -1,295 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// 断点续传下载器
/// </summary>
internal sealed class FileResumeDownloader : DownloaderBase
{
private enum ESteps
{
None,
CheckTempFile,
WaitingCheckTempFile,
PrepareDownload,
CreateDownloader,
CheckDownload,
VerifyTempFile,
WaitingVerifyTempFile,
CachingFile,
TryAgain,
Done,
}
private readonly string _tempFilePath;
private DownloadHandlerFileRange _downloadHandle = null;
private VerifyTempFileOperation _checkFileOp = null;
private VerifyTempFileOperation _verifyFileOp = null;
private ESteps _steps = ESteps.None;
// 重置变量
private ulong _fileOriginLength;
public FileResumeDownloader(BundleInfo bundleInfo, int failedTryAgain, int timeout) : base(bundleInfo, failedTryAgain, timeout)
{
_tempFilePath = bundleInfo.Bundle.TempDataFilePath;
}
public override void SendRequest(params object[] param)
{
if (_steps == ESteps.None)
{
_steps = ESteps.CheckTempFile;
}
}
public override void Update()
{
if (_steps == ESteps.None)
return;
if (IsDone())
return;
// 检测临时文件
if (_steps == ESteps.CheckTempFile)
{
VerifyTempFileElement element = new VerifyTempFileElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize);
_checkFileOp = VerifyTempFileOperation.CreateOperation(element);
OperationSystem.StartOperation(_checkFileOp);
_steps = ESteps.WaitingCheckTempFile;
}
// 等待检测结果
if (_steps == ESteps.WaitingCheckTempFile)
{
if (WaitForAsyncComplete)
_checkFileOp.Update();
if (_checkFileOp.IsDone == false)
return;
if (_checkFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CachingFile;
}
else
{
if (_checkFileOp.VerifyResult == EVerifyResult.FileOverflow)
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
_steps = ESteps.PrepareDownload;
}
}
// 创建下载器
if (_steps == ESteps.PrepareDownload)
{
// 获取请求地址
_requestURL = GetRequestURL();
// 重置变量
_downloadProgress = 0f;
_downloadedBytes = 0;
// 重置变量
_isAbort = false;
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
_fileOriginLength = 0;
// 重置计时器
if (_tryAgainTimer > 0f)
YooLogger.Warning($"Try again download : {_requestURL}");
_tryAgainTimer = 0f;
_steps = ESteps.CreateDownloader;
}
// 创建下载器
if (_steps == ESteps.CreateDownloader)
{
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
_webRequest = DownloadSystem.NewRequest(_requestURL);
var handler = new DownloadHandlerFile(_tempFilePath, true);
handler.removeFileOnAbort = false;
#else
_webRequest = DownloadSystem.NewRequest(_requestURL);
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}-");
_webRequest.SendWebRequest();
_steps = ESteps.CheckDownload;
}
// 检测下载结果
if (_steps == ESteps.CheckDownload)
{
_downloadProgress = _webRequest.downloadProgress;
_downloadedBytes = _fileOriginLength + _webRequest.downloadedBytes;
if (_webRequest.isDone == false)
{
CheckTimeout();
return;
}
bool hasError = false;
// 检查网络错误
#if UNITY_2020_3_OR_NEWER
if (_webRequest.result != UnityWebRequest.Result.Success)
{
hasError = true;
_lastError = _webRequest.error;
_lastCode = _webRequest.responseCode;
}
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
hasError = true;
_lastError = _webRequest.error;
_lastCode = _webRequest.responseCode;
}
#endif
// 如果网络异常
if (hasError)
{
// 注意:下载断点续传文件发生特殊错误码之后删除文件
if (DownloadSystem.ClearFileResponseCodes != null)
{
if (DownloadSystem.ClearFileResponseCodes.Contains(_webRequest.responseCode))
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
_steps = ESteps.TryAgain;
}
else
{
_steps = ESteps.VerifyTempFile;
}
// 最终释放下载器
DisposeWebRequest();
}
// 验证下载文件
if (_steps == ESteps.VerifyTempFile)
{
VerifyTempFileElement element = new VerifyTempFileElement(_bundleInfo.Bundle.TempDataFilePath, _bundleInfo.Bundle.FileCRC, _bundleInfo.Bundle.FileSize);
_verifyFileOp = VerifyTempFileOperation.CreateOperation(element);
OperationSystem.StartOperation(_verifyFileOp);
_steps = ESteps.WaitingVerifyTempFile;
}
// 等待验证完成
if (_steps == ESteps.WaitingVerifyTempFile)
{
if (WaitForAsyncComplete)
_verifyFileOp.Update();
if (_verifyFileOp.IsDone == false)
return;
if (_verifyFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.CachingFile;
}
else
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
_lastError = _verifyFileOp.Error;
_steps = ESteps.TryAgain;
}
}
// 缓存下载文件
if (_steps == ESteps.CachingFile)
{
try
{
CachingFile(_tempFilePath);
_status = EStatus.Succeed;
_steps = ESteps.Done;
_lastError = string.Empty;
_lastCode = 0;
}
catch (Exception e)
{
_lastError = e.Message;
_steps = ESteps.TryAgain;
}
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (_failedTryAgain <= 0)
{
ReportError();
_status = EStatus.Failed;
_steps = ESteps.Done;
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
_failedTryAgain--;
_steps = ESteps.PrepareDownload;
ReportWarning();
}
}
}
public override void Abort()
{
if (IsDone() == false)
{
_status = EStatus.Failed;
_steps = ESteps.Done;
_lastError = "user abort";
_lastCode = 0;
DisposeWebRequest();
}
}
private void DisposeWebRequest()
{
if (_downloadHandle != null)
{
_downloadHandle.Cleanup();
_downloadHandle = null;
}
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
}
}
}
}

View File

@@ -3,209 +3,138 @@ using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal sealed class WebDownloader : DownloaderBase
{
private enum ESteps
{
None,
PrepareDownload,
CreateDownloader,
CheckDownload,
VerifyTempFile,
WaitingVerifyTempFile,
CachingFile,
TryAgain,
Done,
}
internal sealed class WebDownloader : DownloaderBase
{
private enum ESteps
{
None,
PrepareDownload,
CreateDownloader,
CheckDownload,
TryAgain,
Done,
}
private DownloadHandlerAssetBundle _downloadhandler;
private ESteps _steps = ESteps.None;
private bool _getAssetBundle = false;
private AssetBundle _cacheAssetBundle;
private ESteps _steps = ESteps.None;
private bool _getAssetBundle = false;
public WebDownloader(BundleInfo bundleInfo, int failedTryAgain, int timeout) : base(bundleInfo, failedTryAgain, timeout)
{
}
public override void SendRequest(params object[] param)
{
if (_steps == ESteps.None)
{
if (param.Length > 0)
{
_getAssetBundle = (bool)param[0];
}
_steps = ESteps.PrepareDownload;
}
}
public override void Update()
{
if (_steps == ESteps.None)
return;
if (IsDone())
return;
public WebDownloader(BundleInfo bundleInfo, System.Type requesterType, int failedTryAgain, int timeout) : base(bundleInfo, requesterType, failedTryAgain, timeout)
{
}
public override void SendRequest(params object[] args)
{
if (_steps == ESteps.None)
{
if (args.Length > 0)
{
_getAssetBundle = (bool)args[0];
}
_steps = ESteps.PrepareDownload;
}
}
public override void Update()
{
if (_steps == ESteps.None)
return;
if (IsDone())
return;
// 创建下载器
if (_steps == ESteps.PrepareDownload)
{
// 重置变量
_downloadProgress = 0f;
_downloadedBytes = 0;
// 创建下载器
if (_steps == ESteps.PrepareDownload)
{
// 获取请求地址
_requestURL = GetRequestURL();
// 重置变量
_isAbort = false;
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
_tryAgainTimer = 0f;
// 重置变量
DownloadProgress = 0f;
DownloadedBytes = 0;
// 获取请求地址
_requestURL = GetRequestURL();
_steps = ESteps.CreateDownloader;
}
// 重置变量
_isAbort = false;
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
// 创建下载
if (_steps == ESteps.CreateDownloader)
{
_webRequest = DownloadSystem.NewRequest(_requestURL);
// 重置计时
if (_tryAgainTimer > 0f)
YooLogger.Warning($"Try again download : {_requestURL}");
_tryAgainTimer = 0f;
if (CacheSystem.DisableUnityCacheOnWebGL)
{
uint crc = _bundleInfo.Bundle.UnityCRC;
_downloadhandler = new DownloadHandlerAssetBundle(_requestURL, crc);
#if UNITY_2020_3_OR_NEWER
_downloadhandler.autoLoadAssetBundle = false;
#endif
}
else
{
uint crc = _bundleInfo.Bundle.UnityCRC;
var hash = Hash128.Parse(_bundleInfo.Bundle.FileHash);
_downloadhandler = new DownloadHandlerAssetBundle(_requestURL, hash, crc);
#if UNITY_2020_3_OR_NEWER
_downloadhandler.autoLoadAssetBundle = false;
#endif
}
_steps = ESteps.CreateDownloader;
}
_webRequest.downloadHandler = _downloadhandler;
_webRequest.disposeDownloadHandlerOnDispose = false;
_webRequest.SendWebRequest();
_steps = ESteps.CheckDownload;
}
// 创建下载器
if (_steps == ESteps.CreateDownloader)
{
_requester = (IWebRequester)Activator.CreateInstance(_requesterType);
_requester.Create(_requestURL, _bundleInfo, _getAssetBundle);
_steps = ESteps.CheckDownload;
}
// 检测下载结果
if (_steps == ESteps.CheckDownload)
{
_downloadProgress = _webRequest.downloadProgress;
_downloadedBytes = _webRequest.downloadedBytes;
if (_webRequest.isDone == false)
{
CheckTimeout();
return;
}
// 检测下载结果
if (_steps == ESteps.CheckDownload)
{
_requester.Update();
DownloadedBytes = _requester.DownloadedBytes;
DownloadProgress = _requester.DownloadProgress;
if (_requester.IsDone() == false)
{
CheckTimeout();
return;
}
bool hasError = false;
_lastestNetError = _requester.RequestNetError;
_lastestHttpCode = _requester.RequestHttpCode;
if (_requester.Status != ERequestStatus.Success)
{
_steps = ESteps.TryAgain;
}
else
{
_status = EStatus.Succeed;
_steps = ESteps.Done;
}
}
// 检查网络错误
#if UNITY_2020_3_OR_NEWER
if (_webRequest.result != UnityWebRequest.Result.Success)
{
hasError = true;
_lastError = _webRequest.error;
_lastCode = _webRequest.responseCode;
}
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
hasError = true;
_lastError = _webRequest.error;
_lastCode = _webRequest.responseCode;
}
#endif
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (_failedTryAgain <= 0)
{
ReportError();
_status = EStatus.Failed;
_steps = ESteps.Done;
return;
}
// 如果网络异常
if (hasError)
{
_steps = ESteps.TryAgain;
}
else
{
_status = EStatus.Succeed;
_steps = ESteps.Done;
_lastError = string.Empty;
_lastCode = 0;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
_failedTryAgain--;
_steps = ESteps.PrepareDownload;
ReportWarning();
YooLogger.Warning($"Try again download : {_requestURL}");
}
}
}
public override void Abort()
{
if (_requester != null)
_requester.Abort();
if (_getAssetBundle)
{
_cacheAssetBundle = _downloadhandler.assetBundle;
if (_cacheAssetBundle == null)
{
_lastError = "assetBundle is null";
_steps = ESteps.TryAgain;
}
}
// 最终释放请求
DisposeRequest();
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (_failedTryAgain <= 0)
{
DisposeRequest();
ReportError();
_status = EStatus.Failed;
_steps = ESteps.Done;
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
_failedTryAgain--;
_steps = ESteps.PrepareDownload;
ReportWarning();
YooLogger.Warning($"Try again download : {_requestURL}");
}
}
}
public override void Abort()
{
if (IsDone() == false)
{
_status = EStatus.Failed;
_steps = ESteps.Done;
_lastError = "user abort";
_lastCode = 0;
DisposeRequest();
}
}
private void DisposeRequest()
{
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
}
if (_downloadhandler != null)
{
_downloadhandler.Dispose();
_downloadhandler = null;
}
}
/// <summary>
/// 获取资源包
/// </summary>
public AssetBundle GetAssetBundle()
{
return _cacheAssetBundle;
}
}
if (IsDone() == false)
{
_status = EStatus.Failed;
_steps = ESteps.Done;
_lastestNetError = "user abort";
_lastestHttpCode = 0;
}
}
public override AssetBundle GetAssetBundle()
{
return (AssetBundle)_requester.GetRequestObject();
}
}
}

View File

@@ -4,33 +4,33 @@ using System.Collections.Generic;
namespace YooAsset
{
public class RequestHelper
{
/// <summary>
/// 记录网络请求失败事件的次数
/// </summary>
private static readonly Dictionary<string, int> _requestFailedRecorder = new Dictionary<string, int>(1000);
public class RequestHelper
{
/// <summary>
/// 记录网络请求失败事件的次数
/// </summary>
private static readonly Dictionary<string, int> _requestFailedRecorder = new Dictionary<string, int>(1000);
/// <summary>
/// 记录请求失败事件
/// </summary>
public static void RecordRequestFailed(string packageName, string eventName)
{
string key = $"{packageName}_{eventName}";
if (_requestFailedRecorder.ContainsKey(key) == false)
_requestFailedRecorder.Add(key, 0);
_requestFailedRecorder[key]++;
}
/// <summary>
/// 记录请求失败事件
/// </summary>
public static void RecordRequestFailed(string packageName, string eventName)
{
string key = $"{packageName}_{eventName}";
if (_requestFailedRecorder.ContainsKey(key) == false)
_requestFailedRecorder.Add(key, 0);
_requestFailedRecorder[key]++;
}
/// <summary>
/// 获取请求失败的次数
/// </summary>
public static int GetRequestFailedCount(string packageName, string eventName)
{
string key = $"{packageName}_{eventName}";
if (_requestFailedRecorder.ContainsKey(key) == false)
_requestFailedRecorder.Add(key, 0);
return _requestFailedRecorder[key];
}
}
/// <summary>
/// 获取请求失败的次数
/// </summary>
public static int GetRequestFailedCount(string packageName, string eventName)
{
string key = $"{packageName}_{eventName}";
if (_requestFailedRecorder.ContainsKey(key) == false)
_requestFailedRecorder.Add(key, 0);
return _requestFailedRecorder[key];
}
}
}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8a97534752300584a9b8c60c04c6a6a8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,145 @@
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal class AssetBundleWebRequest : IWebRequester
{
private UnityWebRequest _webRequest;
private DownloadHandlerAssetBundle _downloadhandler;
private AssetBundle _cacheAssetBundle;
private bool _getAssetBundle = false;
public ERequestStatus Status { private set; get; } = ERequestStatus.None;
public float DownloadProgress { private set; get; }
public ulong DownloadedBytes { private set; get; }
public string RequestNetError { private set; get; }
public long RequestHttpCode { private set; get; }
public AssetBundleWebRequest() { }
public void Create(string requestURL, BundleInfo bundleInfo, params object[] args)
{
if (Status != ERequestStatus.None)
throw new System.Exception("Should never get here !");
if (args.Length == 0)
throw new System.Exception("Not found param value");
// 解析附加参数
_getAssetBundle = (bool)args[0];
// 创建下载器
_webRequest = DownloadHelper.NewRequest(requestURL);
if (CacheHelper.DisableUnityCacheOnWebGL)
{
uint crc = bundleInfo.Bundle.UnityCRC;
_downloadhandler = new DownloadHandlerAssetBundle(requestURL, crc);
}
else
{
uint crc = bundleInfo.Bundle.UnityCRC;
var hash = Hash128.Parse(bundleInfo.Bundle.FileHash);
_downloadhandler = new DownloadHandlerAssetBundle(requestURL, hash, crc);
}
#if UNITY_2020_3_OR_NEWER
_downloadhandler.autoLoadAssetBundle = false;
#endif
_webRequest.downloadHandler = _downloadhandler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
Status = ERequestStatus.InProgress;
}
public void Update()
{
if (Status == ERequestStatus.None)
return;
if (IsDone())
return;
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = _webRequest.downloadedBytes;
if (_webRequest.isDone == false)
return;
// 检查网络错误
#if UNITY_2020_3_OR_NEWER
RequestHttpCode = _webRequest.responseCode;
if (_webRequest.result != UnityWebRequest.Result.Success)
{
RequestNetError = _webRequest.error;
Status = ERequestStatus.Error;
}
else
{
Status = ERequestStatus.Success;
}
#else
RequestHttpCode = _webRequest.responseCode;
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
RequestNetError = _webRequest.error;
Status = ERequestStatus.Error;
}
else
{
Status = ERequestStatus.Success;
}
#endif
// 缓存加载的AssetBundle对象
if (Status == ERequestStatus.Success)
{
if (_getAssetBundle)
{
_cacheAssetBundle = _downloadhandler.assetBundle;
if (_cacheAssetBundle == null)
{
RequestNetError = "assetBundle is null";
Status = ERequestStatus.Error;
}
}
}
// 最终释放下载器
DisposeWebRequest();
}
public void Abort()
{
// 如果下载任务还未开始
if (Status == ERequestStatus.None)
{
RequestNetError = "user cancel";
Status = ERequestStatus.Error;
}
else
{
// 注意:为了防止同一个文件强制停止之后立马创建新的请求,应该让进行中的请求自然终止。
if (_webRequest != null)
{
if (_webRequest.isDone == false)
_webRequest.Abort(); // If in progress, halts the UnityWebRequest as soon as possible.
}
}
}
public bool IsDone()
{
if (Status == ERequestStatus.Success || Status == ERequestStatus.Error)
return true;
else
return false;
}
public object GetRequestObject()
{
return _cacheAssetBundle;
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 34efa85440da15247b570a98b0971282
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,87 @@
using System;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
/// <summary>
/// 支持Unity2018版本的断点续传下载器
/// </summary>
internal class DownloadHandlerFileRange : DownloadHandlerScript
{
private string _fileSavePath;
private long _fileTotalSize;
private UnityWebRequest _webRequest;
private FileStream _fileStream;
private long _localFileSize = 0;
private long _curFileSize = 0;
public DownloadHandlerFileRange(string fileSavePath, long fileTotalSize, UnityWebRequest webRequest) : base(new byte[1024 * 1024])
{
_fileSavePath = fileSavePath;
_fileTotalSize = fileTotalSize;
_webRequest = webRequest;
if (File.Exists(fileSavePath))
{
FileInfo fileInfo = new FileInfo(fileSavePath);
_localFileSize = fileInfo.Length;
}
_fileStream = new FileStream(_fileSavePath, FileMode.Append, FileAccess.Write);
_curFileSize = _localFileSize;
}
protected override bool ReceiveData(byte[] data, int dataLength)
{
if (data == null || dataLength == 0 || _webRequest.responseCode >= 400)
return false;
if (_fileStream == null)
return false;
_fileStream.Write(data, 0, dataLength);
_curFileSize += dataLength;
return true;
}
/// <summary>
/// UnityWebRequest.downloadHandler.data
/// </summary>
protected override byte[] GetData()
{
return null;
}
/// <summary>
/// UnityWebRequest.downloadHandler.text
/// </summary>
protected override string GetText()
{
return null;
}
/// <summary>
/// UnityWebRequest.downloadProgress
/// </summary>
protected override float GetProgress()
{
return _fileTotalSize == 0 ? 0 : ((float)_curFileSize) / _fileTotalSize;
}
/// <summary>
/// 释放下载句柄
/// </summary>
public void Cleanup()
{
if (_fileStream != null)
{
_fileStream.Flush();
_fileStream.Dispose();
_fileStream = null;
}
}
}
}

View File

@@ -0,0 +1,107 @@
using System.IO;
using UnityEngine.Networking;
namespace YooAsset
{
internal class FileGeneralRequest : IWebRequester
{
private UnityWebRequest _webRequest;
public ERequestStatus Status { private set; get; } = ERequestStatus.None;
public float DownloadProgress { private set; get; }
public ulong DownloadedBytes { private set; get; }
public string RequestNetError { private set; get; }
public long RequestHttpCode { private set; get; }
public FileGeneralRequest() { }
public void Create(string requestURL, BundleInfo bundleInfo, params object[] args)
{
if (Status != ERequestStatus.None)
throw new System.Exception("Should never get here !");
string tempFilePath = bundleInfo.TempDataFilePath;
// 删除临时文件
if (File.Exists(tempFilePath))
File.Delete(tempFilePath);
// 创建下载器
_webRequest = DownloadHelper.NewRequest(requestURL);
DownloadHandlerFile handler = new DownloadHandlerFile(tempFilePath);
handler.removeFileOnAbort = true;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
Status = ERequestStatus.InProgress;
}
public void Update()
{
if (Status == ERequestStatus.None)
return;
if (IsDone())
return;
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = _webRequest.downloadedBytes;
if (_webRequest.isDone == false)
return;
// 检查网络错误
#if UNITY_2020_3_OR_NEWER
RequestHttpCode = _webRequest.responseCode;
if (_webRequest.result != UnityWebRequest.Result.Success)
{
RequestNetError = _webRequest.error;
Status = ERequestStatus.Error;
}
else
{
Status = ERequestStatus.Success;
}
#else
RequestHttpCode = _webRequest.responseCode;
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
RequestNetError = _webRequest.error;
Status = ERequestStatus.Error;
}
else
{
Status = ERequestStatus.Success;
}
#endif
// 最终释放下载器
DisposeWebRequest();
}
public void Abort()
{
DisposeWebRequest();
if (IsDone() == false)
{
RequestNetError = "user abort";
RequestHttpCode = 0;
Status = ERequestStatus.Error;
}
}
public bool IsDone()
{
if (Status == ERequestStatus.Success || Status == ERequestStatus.Error)
return true;
else
return false;
}
public object GetRequestObject()
{
throw new System.NotImplementedException();
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
_webRequest.Dispose(); //注意引擎底层会自动调用Abort方法
_webRequest = null;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b2f339136a4269343a3b5ab80450b889
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,150 @@
using System.IO;
using UnityEngine.Networking;
namespace YooAsset
{
internal class FileResumeRequest : IWebRequester
{
private string _tempFilePath;
private UnityWebRequest _webRequest;
private DownloadHandlerFileRange _downloadHandle;
private ulong _fileOriginLength = 0;
public ERequestStatus Status { private set; get; } = ERequestStatus.None;
public float DownloadProgress { private set; get; }
public ulong DownloadedBytes { private set; get; }
public string RequestNetError { private set; get; }
public long RequestHttpCode { private set; get; }
public FileResumeRequest() { }
public void Create(string requestURL, BundleInfo bundleInfo, params object[] args)
{
if (Status != ERequestStatus.None)
throw new System.Exception("Should never get here !");
_tempFilePath = bundleInfo.TempDataFilePath;
long fileBytes = bundleInfo.Bundle.FileSize;
// 获取下载的起始位置
long fileLength = -1;
if (File.Exists(_tempFilePath))
{
FileInfo fileInfo = new FileInfo(_tempFilePath);
fileLength = fileInfo.Length;
_fileOriginLength = (ulong)fileLength;
DownloadedBytes = _fileOriginLength;
}
// 检测下载起始位置是否有效
if (fileLength >= fileBytes)
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
// 创建下载器
_webRequest = DownloadHelper.NewRequest(requestURL);
#if UNITY_2019_4_OR_NEWER
var handler = new DownloadHandlerFile(_tempFilePath, true);
handler.removeFileOnAbort = false;
#else
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}-");
_webRequest.SendWebRequest();
Status = ERequestStatus.InProgress;
}
public void Update()
{
if (Status == ERequestStatus.None)
return;
if (IsDone())
return;
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = _fileOriginLength + _webRequest.downloadedBytes;
if (_webRequest.isDone == false)
return;
// 检查网络错误
#if UNITY_2020_3_OR_NEWER
RequestHttpCode = _webRequest.responseCode;
if (_webRequest.result != UnityWebRequest.Result.Success)
{
RequestNetError = _webRequest.error;
Status = ERequestStatus.Error;
}
else
{
Status = ERequestStatus.Success;
}
#else
RequestHttpCode = _webRequest.responseCode;
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
RequestNetError = _webRequest.error;
Status = ERequestStatus.Error;
}
else
{
Status = ERequestStatus.Success;
}
#endif
// 注意:下载断点续传文件发生特殊错误码之后删除文件
if (Status == ERequestStatus.Error)
{
if (DownloadHelper.ClearFileResponseCodes != null)
{
if (DownloadHelper.ClearFileResponseCodes.Contains(RequestHttpCode))
{
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
}
// 最终释放下载器
DisposeWebRequest();
}
public void Abort()
{
DisposeWebRequest();
if (IsDone() == false)
{
RequestNetError = "user abort";
RequestHttpCode = 0;
Status = ERequestStatus.Error;
}
}
public bool IsDone()
{
if (Status == ERequestStatus.Success || Status == ERequestStatus.Error)
return true;
else
return false;
}
public object GetRequestObject()
{
throw new System.NotImplementedException();
}
private void DisposeWebRequest()
{
if (_downloadHandle != null)
{
_downloadHandle.Cleanup();
_downloadHandle = null;
}
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 816bf5396d7570e4ab28f1af407bc10f
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,65 @@

namespace YooAsset
{
internal enum ERequestStatus
{
None,
InProgress,
Error,
Success,
}
internal interface IWebRequester
{
/// <summary>
/// 任务状态
/// </summary>
public ERequestStatus Status { get; }
/// <summary>
/// 下载进度0f~1f
/// </summary>
public float DownloadProgress { get; }
/// <summary>
/// 已经下载的总字节数
/// </summary>
public ulong DownloadedBytes { get; }
/// <summary>
/// 返回的网络错误
/// </summary>
public string RequestNetError { get; }
/// <summary>
/// 返回的HTTP CODE
/// </summary>
public long RequestHttpCode { get; }
/// <summary>
/// 创建任务
/// </summary>
public void Create(string url, BundleInfo bundleInfo, params object[] args);
/// <summary>
/// 更新任务
/// </summary>
public void Update();
/// <summary>
/// 终止任务
/// </summary>
public void Abort();
/// <summary>
/// 是否已经完成(无论成功或失败)
/// </summary>
public bool IsDone();
/// <summary>
/// 获取请求的对象
/// </summary>
public object GetRequestObject();
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4d80b965d56870b43af1bf6943015914
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -5,34 +5,34 @@ using System.Threading;
namespace YooAsset
{
/// <summary>
/// 同步其它线程里的回调到主线程里
/// 注意Unity3D中需要设置Scripting Runtime Version为.NET4.6
/// </summary>
internal sealed class ThreadSyncContext : SynchronizationContext
{
private readonly ConcurrentQueue<Action> _safeQueue = new ConcurrentQueue<Action>();
/// <summary>
/// 同步其它线程里的回调到主线程里
/// 注意Unity3D中需要设置Scripting Runtime Version为.NET4.6
/// </summary>
internal sealed class ThreadSyncContext : SynchronizationContext
{
private readonly ConcurrentQueue<Action> _safeQueue = new ConcurrentQueue<Action>();
/// <summary>
/// 更新同步队列
/// </summary>
public void Update()
{
while (true)
{
if (_safeQueue.TryDequeue(out Action action) == false)
return;
action.Invoke();
}
}
/// <summary>
/// 更新同步队列
/// </summary>
public void Update()
{
while (true)
{
if (_safeQueue.TryDequeue(out Action action) == false)
return;
action.Invoke();
}
}
/// <summary>
/// 向同步队列里投递一个回调方法
/// </summary>
public override void Post(SendOrPostCallback callback, object state)
{
Action action = new Action(() => { callback(state); });
_safeQueue.Enqueue(action);
}
}
/// <summary>
/// 向同步队列里投递一个回调方法
/// </summary>
public override void Post(SendOrPostCallback callback, object state)
{
Action action = new Action(() => { callback(state); });
_safeQueue.Enqueue(action);
}
}
}

View File

@@ -6,46 +6,46 @@ using UnityEngine;
namespace YooAsset
{
internal class UnityWebDataRequester : UnityWebRequesterBase
{
/// <summary>
/// 发送GET请求
/// </summary>
public void SendRequest(string url, int timeout = 60)
{
if (_webRequest == null)
{
URL = url;
ResetTimeout(timeout);
internal class UnityWebDataRequester : UnityWebRequesterBase
{
/// <summary>
/// 发送GET请求
/// </summary>
public void SendRequest(string url, int timeout = 60)
{
if (_webRequest == null)
{
URL = url;
ResetTimeout(timeout);
_webRequest = DownloadSystem.NewRequest(URL);
DownloadHandlerBuffer handler = new DownloadHandlerBuffer();
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_operationHandle = _webRequest.SendWebRequest();
}
}
_webRequest = DownloadHelper.NewRequest(URL);
DownloadHandlerBuffer handler = new DownloadHandlerBuffer();
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_operationHandle = _webRequest.SendWebRequest();
}
}
/// <summary>
/// 获取下载的字节数据
/// </summary>
public byte[] GetData()
{
if (_webRequest != null && IsDone())
return _webRequest.downloadHandler.data;
else
return null;
}
/// <summary>
/// 获取下载的字节数据
/// </summary>
public byte[] GetData()
{
if (_webRequest != null && IsDone())
return _webRequest.downloadHandler.data;
else
return null;
}
/// <summary>
/// 获取下载的文本数据
/// </summary>
public string GetText()
{
if (_webRequest != null && IsDone())
return _webRequest.downloadHandler.text;
else
return null;
}
}
/// <summary>
/// 获取下载的文本数据
/// </summary>
public string GetText()
{
if (_webRequest != null && IsDone())
return _webRequest.downloadHandler.text;
else
return null;
}
}
}

View File

@@ -6,25 +6,25 @@ using UnityEngine;
namespace YooAsset
{
internal class UnityWebFileRequester : UnityWebRequesterBase
{
/// <summary>
/// 发送GET请求
/// </summary>
public void SendRequest(string url, string fileSavePath, int timeout = 60)
{
if (_webRequest == null)
{
URL = url;
ResetTimeout(timeout);
internal class UnityWebFileRequester : UnityWebRequesterBase
{
/// <summary>
/// 发送GET请求
/// </summary>
public void SendRequest(string url, string fileSavePath, int timeout = 60)
{
if (_webRequest == null)
{
URL = url;
ResetTimeout(timeout);
_webRequest = DownloadSystem.NewRequest(URL);
DownloadHandlerFile handler = new DownloadHandlerFile(fileSavePath);
handler.removeFileOnAbort = true;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_operationHandle = _webRequest.SendWebRequest();
}
}
}
_webRequest = DownloadHelper.NewRequest(URL);
DownloadHandlerFile handler = new DownloadHandlerFile(fileSavePath);
handler.removeFileOnAbort = true;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_operationHandle = _webRequest.SendWebRequest();
}
}
}
}

View File

@@ -6,111 +6,111 @@ using UnityEngine;
namespace YooAsset
{
internal abstract class UnityWebRequesterBase
{
protected UnityWebRequest _webRequest;
protected UnityWebRequestAsyncOperation _operationHandle;
internal abstract class UnityWebRequesterBase
{
protected UnityWebRequest _webRequest;
protected UnityWebRequestAsyncOperation _operationHandle;
// 超时相关
private float _timeout;
private bool _isAbort = false;
private ulong _latestDownloadBytes;
private float _latestDownloadRealtime;
// 超时相关
private float _timeout;
private bool _isAbort = false;
private ulong _latestDownloadBytes;
private float _latestDownloadRealtime;
/// <summary>
/// 请求URL地址
/// </summary>
public string URL { protected set; get; }
/// <summary>
/// 请求URL地址
/// </summary>
public string URL { protected set; get; }
protected void ResetTimeout(float timeout)
{
_timeout = timeout;
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
}
protected void ResetTimeout(float timeout)
{
_timeout = timeout;
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
}
/// <summary>
/// 释放下载器
/// </summary>
public void Dispose()
{
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
_operationHandle = null;
}
}
/// <summary>
/// 释放下载器
/// </summary>
public void Dispose()
{
if (_webRequest != null)
{
_webRequest.Dispose();
_webRequest = null;
_operationHandle = null;
}
}
/// <summary>
/// 是否完毕(无论成功失败)
/// </summary>
public bool IsDone()
{
if (_operationHandle == null)
return false;
return _operationHandle.isDone;
}
/// <summary>
/// 是否完毕(无论成功失败)
/// </summary>
public bool IsDone()
{
if (_operationHandle == null)
return false;
return _operationHandle.isDone;
}
/// <summary>
/// 下载进度
/// </summary>
public float Progress()
{
if (_operationHandle == null)
return 0;
return _operationHandle.progress;
}
/// <summary>
/// 下载进度
/// </summary>
public float Progress()
{
if (_operationHandle == null)
return 0;
return _operationHandle.progress;
}
/// <summary>
/// 下载是否发生错误
/// </summary>
public bool HasError()
{
/// <summary>
/// 下载是否发生错误
/// </summary>
public bool HasError()
{
#if UNITY_2020_3_OR_NEWER
return _webRequest.result != UnityWebRequest.Result.Success;
return _webRequest.result != UnityWebRequest.Result.Success;
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
return true;
else
return false;
if (_webRequest.isNetworkError || _webRequest.isHttpError)
return true;
else
return false;
#endif
}
}
/// <summary>
/// 获取错误信息
/// </summary>
public string GetError()
{
if (_webRequest != null)
{
return $"URL : {URL} Error : {_webRequest.error}";
}
return string.Empty;
}
/// <summary>
/// 获取错误信息
/// </summary>
public string GetError()
{
if (_webRequest != null)
{
return $"URL : {URL} Error : {_webRequest.error}";
}
return string.Empty;
}
/// <summary>
/// 检测超时
/// </summary>
public void CheckTimeout()
{
// 注意:在连续时间段内无新增下载数据及判定为超时
if (_isAbort == false)
{
if (_latestDownloadBytes != _webRequest.downloadedBytes)
{
_latestDownloadBytes = _webRequest.downloadedBytes;
_latestDownloadRealtime = Time.realtimeSinceStartup;
}
/// <summary>
/// 检测超时
/// </summary>
public void CheckTimeout()
{
// 注意:在连续时间段内无新增下载数据及判定为超时
if (_isAbort == false)
{
if (_latestDownloadBytes != _webRequest.downloadedBytes)
{
_latestDownloadBytes = _webRequest.downloadedBytes;
_latestDownloadRealtime = Time.realtimeSinceStartup;
}
float offset = Time.realtimeSinceStartup - _latestDownloadRealtime;
if (offset > _timeout)
{
_webRequest.Abort();
_isAbort = true;
}
}
}
}
float offset = Time.realtimeSinceStartup - _latestDownloadRealtime;
if (offset > _timeout)
{
_webRequest.Abort();
_isAbort = true;
}
}
}
}
}