update file system

下载器相关逻辑代码重构
This commit is contained in:
何冠峰
2025-07-10 10:58:14 +08:00
parent 236468b4a8
commit b90ab01fe9
26 changed files with 644 additions and 943 deletions

View File

@@ -125,7 +125,7 @@ namespace YooAsset
} }
public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options) public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options)
{ {
// 注意:业务层的解压下载器会依赖内置文件系统的下载方法 // 注意:业务层的解压器会依赖方法
options.ImportFilePath = GetBuildinFileLoadPath(bundle); options.ImportFilePath = GetBuildinFileLoadPath(bundle);
return _unpackFileSystem.DownloadFileAsync(bundle, options); return _unpackFileSystem.DownloadFileAsync(bundle, options);
} }

View File

@@ -166,12 +166,23 @@ namespace YooAsset
} }
public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options) public virtual FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options)
{ {
var downloader = DownloadCenter.DownloadFileAsync(bundle, options); // 获取下载地址
downloader.Reference(); //增加下载器的引用计数 if (string.IsNullOrEmpty(options.ImportFilePath))
{
// 注意:如果是解压文件系统类,这里会返回本地内置文件的下载路径
string mainURL = RemoteServices.GetRemoteMainURL(bundle.FileName);
string fallbackURL = RemoteServices.GetRemoteFallbackURL(bundle.FileName);
options.SetURL(mainURL, fallbackURL);
}
else
{
// 注意:把本地导入文件路径转换为下载器请求地址
string mainURL = DownloadSystemHelper.ConvertToWWWPath(options.ImportFilePath);
options.SetURL(mainURL, mainURL);
}
// 注意:将下载器进行包裹,可以避免父类任务终止的时候,连带子任务里的下载器也一起被终止! var downloader = new DownloadPackageBundleOperation(this, bundle, options);
var wrapper = new DownloadFileWrapper(downloader); return downloader;
return wrapper;
} }
public virtual FSLoadBundleOperation LoadBundleFile(PackageBundle bundle) public virtual FSLoadBundleOperation LoadBundleFile(PackageBundle bundle)
{ {

View File

@@ -55,7 +55,6 @@ namespace YooAsset
if (_steps == ESteps.DownloadFile) if (_steps == ESteps.DownloadFile)
{ {
// 注意边玩边下下载器引用计数没有Release
if (_downloadFileOp == null) if (_downloadFileOp == null)
{ {
DownloadFileOptions options = new DownloadFileOptions(int.MaxValue, 60); DownloadFileOptions options = new DownloadFileOptions(int.MaxValue, 60);
@@ -303,7 +302,6 @@ namespace YooAsset
if (_steps == ESteps.DownloadFile) if (_steps == ESteps.DownloadFile)
{ {
// 注意边玩边下下载器引用计数没有Release
if (_downloadFileOp == null) if (_downloadFileOp == null)
{ {
DownloadFileOptions options = new DownloadFileOptions(int.MaxValue, 60); DownloadFileOptions options = new DownloadFileOptions(int.MaxValue, 60);

View File

@@ -3,10 +3,21 @@ using System.Collections.Generic;
namespace YooAsset namespace YooAsset
{ {
/// <summary>
/// 下载器单元测试
/// 1. 下载失败重试机制
/// 2. 下载引用计数机制
/// 3. 最大下载并发机制
/// 4. 异步下载远端资源
/// 5. 同步下载远端资源
/// 6. 异步拷贝本地资源
/// 7. 同步拷贝本地资源
/// 9. 断点续传下载器
/// </summary>
internal class DownloadCenterOperation : AsyncOperationBase internal class DownloadCenterOperation : AsyncOperationBase
{ {
private readonly DefaultCacheFileSystem _fileSystem; private readonly DefaultCacheFileSystem _fileSystem;
protected readonly Dictionary<string, DefaultDownloadFileOperation> _downloaders = new Dictionary<string, DefaultDownloadFileOperation>(1000); protected readonly Dictionary<string, UnityDownloadFileOperation> _downloaders = new Dictionary<string, UnityDownloadFileOperation>(1000);
protected readonly List<string> _removeDownloadList = new List<string>(1000); protected readonly List<string> _removeDownloadList = new List<string>(1000);
public DownloadCenterOperation(DefaultCacheFileSystem fileSystem) public DownloadCenterOperation(DefaultCacheFileSystem fileSystem)
@@ -74,34 +85,21 @@ namespace YooAsset
/// <summary> /// <summary>
/// 创建下载任务 /// 创建下载任务
/// </summary> /// </summary>
public FSDownloadFileOperation DownloadFileAsync(PackageBundle bundle, DownloadFileOptions options) public UnityDownloadFileOperation DownloadFileAsync(PackageBundle bundle, string url, int timeout)
{ {
// 查询旧的下载器 // 查询旧的下载器
if (_downloaders.TryGetValue(bundle.BundleGUID, out var oldDownloader)) if (_downloaders.TryGetValue(bundle.BundleGUID, out var oldDownloader))
{ {
oldDownloader.Reference();
return oldDownloader; return oldDownloader;
} }
// 获取下载地址
if (string.IsNullOrEmpty(options.ImportFilePath))
{
// 注意:如果是解压文件系统类,这里会返回本地内置文件的下载路径
options.MainURL = _fileSystem.RemoteServices.GetRemoteMainURL(bundle.FileName);
options.FallbackURL = _fileSystem.RemoteServices.GetRemoteFallbackURL(bundle.FileName);
}
else
{
// 注意:把本地导入文件路径转换为下载器请求地址
options.MainURL = DownloadSystemHelper.ConvertToWWWPath(options.ImportFilePath);
options.FallbackURL = options.MainURL;
}
// 创建新的下载器 // 创建新的下载器
DefaultDownloadFileOperation newDownloader; UnityDownloadFileOperation newDownloader;
bool isRequestLocalFile = DownloadSystemHelper.IsRequestLocalFile(options.MainURL); bool isRequestLocalFile = DownloadSystemHelper.IsRequestLocalFile(url);
if (isRequestLocalFile) if (isRequestLocalFile)
{ {
newDownloader = new DownloadLocalFileOperation(_fileSystem, bundle, options); newDownloader = new UnityDownloadLocalFileOperation(_fileSystem, bundle, url, timeout);
AddChildOperation(newDownloader); AddChildOperation(newDownloader);
_downloaders.Add(bundle.BundleGUID, newDownloader); _downloaders.Add(bundle.BundleGUID, newDownloader);
} }
@@ -109,17 +107,19 @@ namespace YooAsset
{ {
if (bundle.FileSize >= _fileSystem.ResumeDownloadMinimumSize) if (bundle.FileSize >= _fileSystem.ResumeDownloadMinimumSize)
{ {
newDownloader = new DownloadResumeFileOperation(_fileSystem, bundle, options); newDownloader = new UnityDownloadResumeFileOperation(_fileSystem, bundle, url, timeout);
AddChildOperation(newDownloader); AddChildOperation(newDownloader);
_downloaders.Add(bundle.BundleGUID, newDownloader); _downloaders.Add(bundle.BundleGUID, newDownloader);
} }
else else
{ {
newDownloader = new DownloadNormalFileOperation(_fileSystem, bundle, options); newDownloader = new UnityDownloadNormalFileOperation(_fileSystem, bundle, url, timeout);
AddChildOperation(newDownloader); AddChildOperation(newDownloader);
_downloaders.Add(bundle.BundleGUID, newDownloader); _downloaders.Add(bundle.BundleGUID, newDownloader);
} }
} }
newDownloader.Reference();
return newDownloader; return newDownloader;
} }

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,11 +0,0 @@
fileFormatVersion: 2
guid: 94254ab8e4496214884c11a891c131c6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,194 +0,0 @@
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal sealed class DownloadNormalFileOperation : DefaultDownloadFileOperation
{
private readonly DefaultCacheFileSystem _fileSystem;
private VerifyTempFileOperation _verifyOperation;
private string _tempFilePath;
private ESteps _steps = ESteps.None;
internal DownloadNormalFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, DownloadFileOptions options) : base(bundle, options)
{
_fileSystem = fileSystem;
}
internal override void InternalStart()
{
_tempFilePath = _fileSystem.GetTempFilePath(Bundle);
_steps = ESteps.CheckExists;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 检测文件是否存在
if (_steps == ESteps.CheckExists)
{
if (_fileSystem.Exists(Bundle))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.CreateRequest;
}
}
// 创建下载器
if (_steps == ESteps.CreateRequest)
{
FileUtility.CreateFileDirectory(_tempFilePath);
// 删除临时文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
// 获取请求地址
_requestURL = GetRequestURL();
// 重置请求
ResetRequestFiled();
// 创建下载器
CreateWebRequest();
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
if (_webRequest.isDone == false)
{
CheckRequestTimeout();
return;
}
// 检查网络错误
if (CheckRequestResult())
_steps = ESteps.VerifyTempFile;
else
_steps = ESteps.TryAgain;
// 注意:最终释放请求器
DisposeWebRequest();
}
// 验证下载文件
if (_steps == ESteps.VerifyTempFile)
{
var element = new TempFileElement(_tempFilePath, Bundle.FileCRC, Bundle.FileSize);
_verifyOperation = new VerifyTempFileOperation(element);
_verifyOperation.StartOperation();
AddChildOperation(_verifyOperation);
_steps = ESteps.CheckVerifyTempFile;
}
// 等待验证完成
if (_steps == ESteps.CheckVerifyTempFile)
{
if (IsWaitForAsyncComplete)
_verifyOperation.WaitForAsyncComplete();
_verifyOperation.UpdateOperation();
if (_verifyOperation.IsDone == false)
return;
if (_verifyOperation.Status == EOperationStatus.Succeed)
{
if (_fileSystem.WriteCacheBundleFile(Bundle, _tempFilePath))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"{_fileSystem.GetType().FullName} failed to write file !";
YooLogger.Error(Error);
}
}
else
{
_steps = ESteps.TryAgain;
Error = _verifyOperation.Error;
}
// 注意:验证完成后直接删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (FailedTryAgain <= 0)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
FailedTryAgain--;
_steps = ESteps.CreateRequest;
YooLogger.Warning(Error);
}
}
}
internal override void InternalAbort()
{
_steps = ESteps.Done;
DisposeWebRequest();
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
//TODO 尝试同步加载远端的资源文件失败
if (Status == EOperationStatus.Failed)
{
YooLogger.Error($"Try load bundle {Bundle.BundleName} from remote !");
YooLogger.Error($"The load remote bundle url : {_requestURL}");
}
_steps = ESteps.Done;
break;
}
}
}
private void CreateWebRequest()
{
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath);
handler.removeFileOnAbort = true;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
}
}

View File

@@ -0,0 +1,164 @@
using UnityEngine;
namespace YooAsset
{
internal class DownloadPackageBundleOperation : FSDownloadFileOperation
{
protected enum ESteps
{
None,
CheckExists,
CreateRequest,
CheckRequest,
TryAgain,
Done,
}
// 下载参数
protected readonly DefaultCacheFileSystem _fileSystem;
protected readonly DownloadFileOptions _options;
private UnityDownloadFileOperation _unityDownloadFileOp;
protected int _requestCount = 0;
protected float _tryAgainTimer;
protected int _failedTryAgain;
private ESteps _steps = ESteps.None;
internal DownloadPackageBundleOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, DownloadFileOptions options) : base(bundle)
{
_fileSystem = fileSystem;
_options = options;
_failedTryAgain = options.FailedTryAgain;
}
internal override void InternalStart()
{
_steps = ESteps.CheckExists;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 检测文件是否存在
if (_steps == ESteps.CheckExists)
{
if (_fileSystem.Exists(Bundle))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.CreateRequest;
}
}
// 创建下载器
if (_steps == ESteps.CreateRequest)
{
if (_options.IsValid() == false)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = "Download file options is invalid !";
Debug.Log(Error);
return;
}
string url = GetRequestURL();
_unityDownloadFileOp = _fileSystem.DownloadCenter.DownloadFileAsync(Bundle, url, _options.Timeout);
_steps = ESteps.CheckRequest;
}
// 检测下载结果
if (_steps == ESteps.CheckRequest)
{
if (IsWaitForAsyncComplete)
_unityDownloadFileOp.WaitForAsyncComplete();
// 因为并发数量限制,下载器可能被挂起!
if (_unityDownloadFileOp.Status == EOperationStatus.None)
return;
_unityDownloadFileOp.UpdateOperation();
Progress = _unityDownloadFileOp.Progress;
DownloadedBytes = _unityDownloadFileOp.DownloadedBytes;
DownloadProgress = _unityDownloadFileOp.DownloadProgress;
if (_unityDownloadFileOp.IsDone == false)
return;
if (_unityDownloadFileOp.Status == EOperationStatus.Succeed)
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
if (IsWaitForAsyncComplete == false && _failedTryAgain > 0)
{
_steps = ESteps.TryAgain;
YooLogger.Warning($"Failed download : {_unityDownloadFileOp.URL} Try again !");
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _unityDownloadFileOp.Error;
YooLogger.Error(Error);
}
}
}
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
_tryAgainTimer = 0f;
_failedTryAgain--;
Progress = 0f;
DownloadProgress = 0f;
DownloadedBytes = 0;
_steps = ESteps.CreateRequest;
}
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
internal override void InternalAbort()
{
// 注意:取消下载任务的时候引用计数减一
if (_steps != ESteps.Done)
{
if (_unityDownloadFileOp != null)
{
_unityDownloadFileOp.Release();
}
}
}
/// <summary>
/// 获取网络请求地址
/// </summary>
protected string GetRequestURL()
{
// 轮流返回请求地址
_requestCount++;
if (_requestCount % 2 == 0)
return _options.FallbackURL;
else
return _options.MainURL;
}
}
}

View File

@@ -0,0 +1,52 @@

namespace YooAsset
{
internal abstract class UnityDownloadFileOperation : UnityWebRequestOperation
{
protected enum ESteps
{
None,
CreateRequest,
Download,
CopyLocalFile,
VerifyFile,
Done,
}
protected readonly DefaultCacheFileSystem _fileSystem;
protected readonly PackageBundle _bundle;
protected readonly string _tempFilePath;
/// <summary>
/// 引用计数
/// </summary>
public int RefCount { private set; get; }
internal UnityDownloadFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, string url, int timeout) : base(url, timeout)
{
_fileSystem = fileSystem;
_bundle = bundle;
_tempFilePath = _fileSystem.GetTempFilePath(bundle);
}
internal override string InternalGetDesc()
{
return $"RefCount : {RefCount}";
}
/// <summary>
/// 减少引用计数
/// </summary>
public void Release()
{
RefCount--;
}
/// <summary>
/// 增加引用计数
/// </summary>
public void Reference()
{
RefCount++;
}
}
}

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2 fileFormatVersion: 2
guid: 8088863fc7dfbd441bc897380cd7b97f guid: 40bb5e9391f413c42ae70e48ca90c4b7
MonoImporter: MonoImporter:
externalObjects: {} externalObjects: {}
serializedVersion: 2 serializedVersion: 2

View File

@@ -4,67 +4,41 @@ using UnityEngine.Networking;
namespace YooAsset namespace YooAsset
{ {
internal class DownloadLocalFileOperation : DefaultDownloadFileOperation internal class UnityDownloadLocalFileOperation : UnityDownloadFileOperation
{ {
private readonly DefaultCacheFileSystem _fileSystem;
private VerifyTempFileOperation _verifyOperation; private VerifyTempFileOperation _verifyOperation;
private string _tempFilePath;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
internal DownloadLocalFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, DownloadFileOptions options) : base(bundle, options) internal UnityDownloadLocalFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, string url, int timeout = 60)
: base(fileSystem, bundle, url, timeout)
{ {
_fileSystem = fileSystem;
} }
internal override void InternalStart() internal override void InternalStart()
{ {
_tempFilePath = _fileSystem.GetTempFilePath(Bundle); if (_fileSystem.CopyLocalFileServices != null)
_steps = ESteps.CheckExists; _steps = ESteps.CopyLocalFile;
else
_steps = ESteps.CreateRequest;
} }
internal override void InternalUpdate() internal override void InternalUpdate()
{ {
if (_steps == ESteps.None || _steps == ESteps.Done) if (_steps == ESteps.None || _steps == ESteps.Done)
return; return;
// 检测文件是否存在
if (_steps == ESteps.CheckExists)
{
if (_fileSystem.Exists(Bundle))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
if (_fileSystem.CopyLocalFileServices != null)
_steps = ESteps.CopyBuildinBundle;
else
_steps = ESteps.CreateRequest;
}
}
// 创建下载器 // 创建下载器
if (_steps == ESteps.CreateRequest) if (_steps == ESteps.CreateRequest)
{ {
FileUtility.CreateFileDirectory(_tempFilePath); FileUtility.CreateFileDirectory(_tempFilePath);
// 删除临时文件
if (File.Exists(_tempFilePath)) if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath); File.Delete(_tempFilePath);
// 获取请求地址 ResetTimeout();
_requestURL = GetRequestURL();
// 重置请求
ResetRequestFiled();
// 创建下载器
CreateWebRequest(); CreateWebRequest();
_steps = ESteps.Download;
_steps = ESteps.CheckRequest;
} }
// 检测下载结果 // 检测下载结果
if (_steps == ESteps.CheckRequest) if (_steps == ESteps.Download)
{ {
DownloadProgress = _webRequest.downloadProgress; DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes; DownloadedBytes = (long)_webRequest.downloadedBytes;
@@ -77,67 +51,67 @@ namespace YooAsset
// 检查网络错误 // 检查网络错误
if (CheckRequestResult()) if (CheckRequestResult())
_steps = ESteps.VerifyTempFile; {
_steps = ESteps.VerifyFile;
}
else else
_steps = ESteps.TryAgain; {
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
// 注意:最终释放请求器 // 注意:最终释放请求器
DisposeWebRequest(); DisposeRequest();
} }
// 拷贝内置文件 // 拷贝内置文件
if (_steps == ESteps.CopyBuildinBundle) if (_steps == ESteps.CopyLocalFile)
{ {
FileUtility.CreateFileDirectory(_tempFilePath); FileUtility.CreateFileDirectory(_tempFilePath);
// 删除临时文件
if (File.Exists(_tempFilePath)) if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath); File.Delete(_tempFilePath);
// 获取请求地址
_requestURL = GetRequestURL();
try try
{ {
//TODO 团结引擎,在某些机型(红米),拷贝包内文件会小概率失败!需要借助其它方式来拷贝包内文件。 //TODO 团结引擎,在某些机型(红米),拷贝包内文件会小概率失败!需要借助其它方式来拷贝包内文件。
var localFileInfo = new LocalFileInfo(); var localFileInfo = new LocalFileInfo();
localFileInfo.PackageName = _fileSystem.PackageName; localFileInfo.PackageName = _fileSystem.PackageName;
localFileInfo.BundleName = Bundle.BundleName; localFileInfo.BundleName = _bundle.BundleName;
localFileInfo.SourceFileURL = _requestURL; localFileInfo.SourceFileURL = _requestURL;
_fileSystem.CopyLocalFileServices.CopyFile(localFileInfo, _tempFilePath); _fileSystem.CopyLocalFileServices.CopyFile(localFileInfo, _tempFilePath);
if (File.Exists(_tempFilePath)) if (File.Exists(_tempFilePath))
{ {
DownloadProgress = 1f; DownloadProgress = 1f;
DownloadedBytes = Bundle.FileSize; DownloadedBytes = _bundle.FileSize;
Progress = DownloadProgress; Progress = DownloadProgress;
_steps = ESteps.VerifyTempFile; _steps = ESteps.VerifyFile;
} }
else else
{ {
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed copy local file : {_requestURL}"; Error = $"Failed copy local file : {_requestURL}";
_steps = ESteps.TryAgain;
} }
} }
catch (System.Exception ex) catch (System.Exception ex)
{ {
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"Failed copy local file : {ex.Message}"; Error = $"Failed copy local file : {ex.Message}";
_steps = ESteps.TryAgain;
} }
} }
// 验证下载文件 // 验证下载文件
if (_steps == ESteps.VerifyTempFile) if (_steps == ESteps.VerifyFile)
{ {
var element = new TempFileElement(_tempFilePath, Bundle.FileCRC, Bundle.FileSize); if (_verifyOperation == null)
_verifyOperation = new VerifyTempFileOperation(element); {
_verifyOperation.StartOperation(); var element = new TempFileElement(_tempFilePath, _bundle.FileCRC, _bundle.FileSize);
AddChildOperation(_verifyOperation); _verifyOperation = new VerifyTempFileOperation(element);
_steps = ESteps.CheckVerifyTempFile; _verifyOperation.StartOperation();
} AddChildOperation(_verifyOperation);
}
// 等待验证完成
if (_steps == ESteps.CheckVerifyTempFile)
{
if (IsWaitForAsyncComplete) if (IsWaitForAsyncComplete)
_verifyOperation.WaitForAsyncComplete(); _verifyOperation.WaitForAsyncComplete();
@@ -147,7 +121,7 @@ namespace YooAsset
if (_verifyOperation.Status == EOperationStatus.Succeed) if (_verifyOperation.Status == EOperationStatus.Succeed)
{ {
if (_fileSystem.WriteCacheBundleFile(Bundle, _tempFilePath)) if (_fileSystem.WriteCacheBundleFile(_bundle, _tempFilePath))
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
Status = EOperationStatus.Succeed; Status = EOperationStatus.Succeed;
@@ -157,12 +131,12 @@ namespace YooAsset
_steps = ESteps.Done; _steps = ESteps.Done;
Status = EOperationStatus.Failed; Status = EOperationStatus.Failed;
Error = $"{_fileSystem.GetType().FullName} failed to write file !"; Error = $"{_fileSystem.GetType().FullName} failed to write file !";
YooLogger.Error(Error);
} }
} }
else else
{ {
_steps = ESteps.TryAgain; _steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _verifyOperation.Error; Error = _verifyOperation.Error;
} }
@@ -170,20 +144,6 @@ namespace YooAsset
if (File.Exists(_tempFilePath)) if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath); File.Delete(_tempFilePath);
} }
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
//TODO 拷贝本地文件失败后不再尝试!
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
}
}
internal override void InternalAbort()
{
_steps = ESteps.Done;
DisposeWebRequest();
} }
internal override void InternalWaitForAsyncComplete() internal override void InternalWaitForAsyncComplete()
{ {
@@ -208,14 +168,5 @@ namespace YooAsset
_webRequest.disposeDownloadHandlerOnDispose = true; _webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest(); _webRequest.SendWebRequest();
} }
private void DisposeWebRequest()
{
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
} }
} }

View File

@@ -0,0 +1,126 @@
using System.IO;
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal sealed class UnityDownloadNormalFileOperation : UnityDownloadFileOperation
{
private VerifyTempFileOperation _verifyOperation;
private ESteps _steps = ESteps.None;
internal UnityDownloadNormalFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, string url, int timeout = 60)
: base(fileSystem, bundle, url, timeout)
{
}
internal override void InternalStart()
{
_steps = ESteps.CreateRequest;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
// 创建下载器
if (_steps == ESteps.CreateRequest)
{
FileUtility.CreateFileDirectory(_tempFilePath);
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
ResetTimeout();
CreateWebRequest();
_steps = ESteps.Download;
}
// 检测下载结果
if (_steps == ESteps.Download)
{
DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
if (_webRequest.isDone == false)
{
CheckRequestTimeout();
return;
}
// 检查网络错误
if (CheckRequestResult())
{
_steps = ESteps.VerifyFile;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
// 注意:最终释放请求器
DisposeRequest();
}
// 验证下载文件
if (_steps == ESteps.VerifyFile)
{
if (_verifyOperation == null)
{
var element = new TempFileElement(_tempFilePath, _bundle.FileCRC, _bundle.FileSize);
_verifyOperation = new VerifyTempFileOperation(element);
_verifyOperation.StartOperation();
AddChildOperation(_verifyOperation);
}
if (IsWaitForAsyncComplete)
_verifyOperation.WaitForAsyncComplete();
_verifyOperation.UpdateOperation();
if (_verifyOperation.IsDone == false)
return;
if (_verifyOperation.Status == EOperationStatus.Succeed)
{
if (_fileSystem.WriteCacheBundleFile(_bundle, _tempFilePath))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"{_fileSystem.GetType().FullName} failed to write file ! {_tempFilePath}";
}
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _verifyOperation.Error;
}
// 注意:验证完成后直接删除文件
if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath);
}
}
internal override void InternalWaitForAsyncComplete()
{
if (_steps != ESteps.Done)
{
YooLogger.Error($"Try load bundle {_bundle.BundleName} from remote : {_requestURL} !");
}
}
private void CreateWebRequest()
{
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
DownloadHandlerFile handler = new DownloadHandlerFile(_tempFilePath);
handler.removeFileOnAbort = true;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
}
}
}

View File

@@ -4,63 +4,38 @@ using UnityEngine.Networking;
namespace YooAsset namespace YooAsset
{ {
internal sealed class DownloadResumeFileOperation : DefaultDownloadFileOperation internal sealed class UnityDownloadResumeFileOperation : UnityDownloadFileOperation
{ {
private readonly DefaultCacheFileSystem _fileSystem;
private DownloadHandlerFileRange _downloadHandle;
private VerifyTempFileOperation _verifyOperation; private VerifyTempFileOperation _verifyOperation;
private long _fileOriginLength = 0; private long _fileOriginLength = 0;
private string _tempFilePath;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
internal DownloadResumeFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, DownloadFileOptions options) : base(bundle, options) internal UnityDownloadResumeFileOperation(DefaultCacheFileSystem fileSystem, PackageBundle bundle, string url, int timeout = 60)
: base(fileSystem, bundle, url, timeout)
{ {
_fileSystem = fileSystem;
} }
internal override void InternalStart() internal override void InternalStart()
{ {
_tempFilePath = _fileSystem.GetTempFilePath(Bundle); _steps = ESteps.CreateRequest;
_steps = ESteps.CheckExists;
} }
internal override void InternalUpdate() internal override void InternalUpdate()
{ {
if (_steps == ESteps.None || _steps == ESteps.Done) if (_steps == ESteps.None || _steps == ESteps.Done)
return; return;
// 检测文件是否存在
if (_steps == ESteps.CheckExists)
{
if (_fileSystem.Exists(Bundle))
{
_steps = ESteps.Done;
Status = EOperationStatus.Succeed;
}
else
{
_steps = ESteps.CreateRequest;
}
}
// 创建下载器 // 创建下载器
if (_steps == ESteps.CreateRequest) if (_steps == ESteps.CreateRequest)
{ {
FileUtility.CreateFileDirectory(_tempFilePath); FileUtility.CreateFileDirectory(_tempFilePath);
// 获取请求地址
_requestURL = GetRequestURL();
// 重置变量
ResetRequestFiled();
// 获取下载起始位置 // 获取下载起始位置
_fileOriginLength = 0; _fileOriginLength = 0;
long fileBeginLength = -1; long fileBeginLength = -1;
if (File.Exists(_tempFilePath)) if (File.Exists(_tempFilePath))
{ {
FileInfo fileInfo = new FileInfo(_tempFilePath); FileInfo fileInfo = new FileInfo(_tempFilePath);
if (fileInfo.Length >= Bundle.FileSize) if (fileInfo.Length >= _bundle.FileSize)
{ {
// 删除临时文件
File.Delete(_tempFilePath); File.Delete(_tempFilePath);
} }
else else
@@ -71,17 +46,17 @@ namespace YooAsset
} }
} }
// 创建下载器 ResetTimeout();
CreateWebRequest(fileBeginLength); CreateWebRequest(fileBeginLength);
_steps = ESteps.Download;
_steps = ESteps.CheckRequest;
} }
// 检测下载结果 // 检测下载结果
if (_steps == ESteps.CheckRequest) if (_steps == ESteps.Download)
{ {
DownloadProgress = _webRequest.downloadProgress; DownloadProgress = _webRequest.downloadProgress;
DownloadedBytes = _fileOriginLength + (long)_webRequest.downloadedBytes; DownloadedBytes = _fileOriginLength + (long)_webRequest.downloadedBytes;
Progress = DownloadProgress;
if (_webRequest.isDone == false) if (_webRequest.isDone == false)
{ {
CheckRequestTimeout(); CheckRequestTimeout();
@@ -90,30 +65,33 @@ namespace YooAsset
// 检查网络错误 // 检查网络错误
if (CheckRequestResult()) if (CheckRequestResult())
_steps = ESteps.VerifyTempFile; {
_steps = ESteps.VerifyFile;
}
else else
_steps = ESteps.TryAgain; {
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
}
// 在遇到特殊错误的时候删除文件 // 在遇到特殊错误的时候删除文件
ClearTempFileWhenError(); ClearTempFileWhenError();
// 注意:最终释放请求器 // 注意:最终释放请求器
DisposeWebRequest(); DisposeRequest();
} }
// 验证下载文件 // 验证下载文件
if (_steps == ESteps.VerifyTempFile) if (_steps == ESteps.VerifyFile)
{ {
var element = new TempFileElement(_tempFilePath, Bundle.FileCRC, Bundle.FileSize); if (_verifyOperation == null)
_verifyOperation = new VerifyTempFileOperation(element); {
_verifyOperation.StartOperation(); var element = new TempFileElement(_tempFilePath, _bundle.FileCRC, _bundle.FileSize);
AddChildOperation(_verifyOperation); _verifyOperation = new VerifyTempFileOperation(element);
_steps = ESteps.CheckVerifyTempFile; _verifyOperation.StartOperation();
} AddChildOperation(_verifyOperation);
}
// 等待验证完成
if (_steps == ESteps.CheckVerifyTempFile)
{
if (IsWaitForAsyncComplete) if (IsWaitForAsyncComplete)
_verifyOperation.WaitForAsyncComplete(); _verifyOperation.WaitForAsyncComplete();
@@ -123,104 +101,38 @@ namespace YooAsset
if (_verifyOperation.Status == EOperationStatus.Succeed) if (_verifyOperation.Status == EOperationStatus.Succeed)
{ {
if (_fileSystem.WriteCacheBundleFile(Bundle, _tempFilePath)) if (_fileSystem.WriteCacheBundleFile(_bundle, _tempFilePath))
{ {
Status = EOperationStatus.Succeed;
_steps = ESteps.Done; _steps = ESteps.Done;
Status = EOperationStatus.Succeed;
} }
else else
{ {
Error = $"{_fileSystem.GetType().FullName} failed to write file !";
Status = EOperationStatus.Failed;
_steps = ESteps.Done; _steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"{_fileSystem.GetType().FullName} failed to write file !";
} }
} }
else else
{ {
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _verifyOperation.Error; Error = _verifyOperation.Error;
_steps = ESteps.TryAgain;
} }
// 注意:验证完成后直接删除文件 // 注意:验证完成后直接删除文件
if (File.Exists(_tempFilePath)) if (File.Exists(_tempFilePath))
File.Delete(_tempFilePath); File.Delete(_tempFilePath);
} }
// 重新尝试下载
if (_steps == ESteps.TryAgain)
{
if (FailedTryAgain <= 0)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
_tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f)
{
FailedTryAgain--;
_steps = ESteps.CreateRequest;
YooLogger.Warning(Error);
}
}
}
internal override void InternalAbort()
{
_steps = ESteps.Done;
DisposeWebRequest();
} }
internal override void InternalWaitForAsyncComplete() internal override void InternalWaitForAsyncComplete()
{ {
while (true) if (_steps != ESteps.Done)
{ {
if (ExecuteWhileDone()) YooLogger.Error($"Try load bundle {_bundle.BundleName} from remote : {_requestURL} !");
{
//TODO 尝试同步加载远端的资源文件失败
if (Status == EOperationStatus.Failed)
{
YooLogger.Error($"Try load bundle {Bundle.BundleName} from remote !");
YooLogger.Error($"The load remote bundle url : {_requestURL}");
}
_steps = ESteps.Done;
break;
}
} }
} }
private void CreateWebRequest(long beginLength)
{
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
#if UNITY_2019_4_OR_NEWER
var handler = new DownloadHandlerFile(_tempFilePath, true);
handler.removeFileOnAbort = false;
#else
var handler = new DownloadHandlerFileRange(FileSavePath, Bundle.FileSize, _webRequest);
_downloadHandle = handler;
#endif
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
if (beginLength > 0)
_webRequest.SetRequestHeader("Range", $"bytes={beginLength}-");
_webRequest.SendWebRequest();
}
private void DisposeWebRequest()
{
if (_downloadHandle != null)
{
_downloadHandle.Cleanup();
_downloadHandle = null;
}
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
private void ClearTempFileWhenError() private void ClearTempFileWhenError()
{ {
if (_fileSystem.ResumeDownloadResponseCodes == null) if (_fileSystem.ResumeDownloadResponseCodes == null)
@@ -233,5 +145,16 @@ namespace YooAsset
File.Delete(_tempFilePath); File.Delete(_tempFilePath);
} }
} }
private void CreateWebRequest(long fileBeginLength)
{
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
var handler = new DownloadHandlerFile(_tempFilePath, true);
handler.removeFileOnAbort = false;
_webRequest.downloadHandler = handler;
_webRequest.disposeDownloadHandlerOnDispose = true;
if (fileBeginLength > 0)
_webRequest.SetRequestHeader("Range", $"bytes={fileBeginLength}-");
_webRequest.SendWebRequest();
}
} }
} }

View File

@@ -34,19 +34,20 @@ namespace YooAsset
{ {
if (_downloadAssetBundleOp == null) if (_downloadAssetBundleOp == null)
{ {
string mainURL = _fileSystem.RemoteServices.GetRemoteMainURL(_bundle.FileName);
string fallbackURL = _fileSystem.RemoteServices.GetRemoteFallbackURL(_bundle.FileName);
DownloadFileOptions options = new DownloadFileOptions(int.MaxValue, 60); DownloadFileOptions options = new DownloadFileOptions(int.MaxValue, 60);
options.MainURL = _fileSystem.RemoteServices.GetRemoteMainURL(_bundle.FileName); options.SetURL(mainURL, fallbackURL);
options.FallbackURL = _fileSystem.RemoteServices.GetRemoteFallbackURL(_bundle.FileName);
if (_bundle.Encrypted) if (_bundle.Encrypted)
{ {
_downloadAssetBundleOp = new DownloadWebEncryptAssetBundleOperation(true, _fileSystem.DecryptionServices, _bundle, options); _downloadAssetBundleOp = new DownloadEncryptAssetBundleOperation(_bundle, options, true, _fileSystem.DecryptionServices);
_downloadAssetBundleOp.StartOperation(); _downloadAssetBundleOp.StartOperation();
AddChildOperation(_downloadAssetBundleOp); AddChildOperation(_downloadAssetBundleOp);
} }
else else
{ {
_downloadAssetBundleOp = new DownloadWebNormalAssetBundleOperation(_fileSystem.DisableUnityWebCache, _bundle, options); _downloadAssetBundleOp = new DownloadNormalAssetBundleOperation(_bundle, options, _fileSystem.DisableUnityWebCache);
_downloadAssetBundleOp.StartOperation(); _downloadAssetBundleOp.StartOperation();
AddChildOperation(_downloadAssetBundleOp); AddChildOperation(_downloadAssetBundleOp);
} }

View File

@@ -34,20 +34,20 @@ namespace YooAsset
{ {
if (_downloadAssetBundleOp == null) if (_downloadAssetBundleOp == null)
{ {
DownloadFileOptions options = new DownloadFileOptions(int.MaxValue, 60);
string fileLoadPath = _fileSystem.GetWebFileLoadPath(_bundle); string fileLoadPath = _fileSystem.GetWebFileLoadPath(_bundle);
options.MainURL = DownloadSystemHelper.ConvertToWWWPath(fileLoadPath); string mainURL = DownloadSystemHelper.ConvertToWWWPath(fileLoadPath);
options.FallbackURL = options.MainURL; DownloadFileOptions options = new DownloadFileOptions(int.MaxValue, 60);
options.SetURL(mainURL, mainURL);
if (_bundle.Encrypted) if (_bundle.Encrypted)
{ {
_downloadAssetBundleOp = new DownloadWebEncryptAssetBundleOperation(true, _fileSystem.DecryptionServices, _bundle, options); _downloadAssetBundleOp = new DownloadEncryptAssetBundleOperation(_bundle, options, true, _fileSystem.DecryptionServices);
_downloadAssetBundleOp.StartOperation(); _downloadAssetBundleOp.StartOperation();
AddChildOperation(_downloadAssetBundleOp); AddChildOperation(_downloadAssetBundleOp);
} }
else else
{ {
_downloadAssetBundleOp = new DownloadWebNormalAssetBundleOperation(_fileSystem.DisableUnityWebCache, _bundle, options); _downloadAssetBundleOp = new DownloadNormalAssetBundleOperation(_bundle, options, _fileSystem.DisableUnityWebCache);
_downloadAssetBundleOp.StartOperation(); _downloadAssetBundleOp.StartOperation();
AddChildOperation(_downloadAssetBundleOp); AddChildOperation(_downloadAssetBundleOp);
} }

View File

@@ -16,12 +16,12 @@ namespace YooAsset
/// <summary> /// <summary>
/// 主资源地址 /// 主资源地址
/// </summary> /// </summary>
public string MainURL { set; get; } public string MainURL { private set; get; }
/// <summary> /// <summary>
/// 备用资源地址 /// 备用资源地址
/// </summary> /// </summary>
public string FallbackURL { set; get; } public string FallbackURL { private set; get; }
/// <summary> /// <summary>
/// 拷贝的本地文件路径 /// 拷贝的本地文件路径
@@ -33,22 +33,32 @@ namespace YooAsset
FailedTryAgain = failedTryAgain; FailedTryAgain = failedTryAgain;
Timeout = timeout; Timeout = timeout;
} }
/// <summary>
/// 设置下载地址
/// </summary>
public void SetURL(string mainURL, string fallbackURL)
{
MainURL = mainURL;
FallbackURL = fallbackURL;
}
/// <summary>
/// 是否有效
/// </summary>
public bool IsValid()
{
if (string.IsNullOrEmpty(MainURL) || string.IsNullOrEmpty(FallbackURL))
return false;
return true;
}
} }
internal abstract class FSDownloadFileOperation : AsyncOperationBase internal abstract class FSDownloadFileOperation : AsyncOperationBase
{ {
public PackageBundle Bundle { private set; get; } public PackageBundle Bundle { private set; get; }
/// <summary>
/// 引用计数
/// </summary>
public int RefCount { private set; get; }
/// <summary>
/// HTTP返回码
/// </summary>
public long HttpCode { protected set; get; }
/// <summary> /// <summary>
/// 当前下载的字节数 /// 当前下载的字节数
/// </summary> /// </summary>
@@ -63,31 +73,8 @@ namespace YooAsset
public FSDownloadFileOperation(PackageBundle bundle) public FSDownloadFileOperation(PackageBundle bundle)
{ {
Bundle = bundle; Bundle = bundle;
RefCount = 0;
HttpCode = 0;
DownloadedBytes = 0; DownloadedBytes = 0;
DownloadProgress = 0; DownloadProgress = 0;
} }
internal override string InternalGetDesc()
{
return $"RefCount : {RefCount}";
}
/// <summary>
/// 减少引用计数
/// </summary>
public virtual void Release()
{
RefCount--;
}
/// <summary>
/// 增加引用计数
/// </summary>
public virtual void Reference()
{
RefCount++;
}
} }
} }

View File

@@ -1,131 +0,0 @@
using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset
{
internal abstract class DefaultDownloadFileOperation : FSDownloadFileOperation
{
protected enum ESteps
{
None,
CheckExists,
CreateRequest,
CheckRequest,
CopyBuildinBundle,
VerifyTempFile,
CheckVerifyTempFile,
TryAgain,
Done,
}
// 下载参数
protected readonly DownloadFileOptions Options;
// 请求相关
protected UnityWebRequest _webRequest;
protected string _requestURL;
protected int _requestCount = 0;
// 超时相关
protected bool _isAbort = false;
protected long _latestDownloadBytes;
protected float _latestDownloadRealtime;
protected float _tryAgainTimer;
// 失败相关
protected int FailedTryAgain;
internal DefaultDownloadFileOperation(PackageBundle bundle, DownloadFileOptions options) : base(bundle)
{
Options = options;
FailedTryAgain = options.FailedTryAgain;
}
/// <summary>
/// 获取网络请求地址
/// </summary>
protected string GetRequestURL()
{
// 轮流返回请求地址
_requestCount++;
if (_requestCount % 2 == 0)
return Options.FallbackURL;
else
return Options.MainURL;
}
/// <summary>
/// 重置请求字段
/// </summary>
protected void ResetRequestFiled()
{
// 重置变量
_isAbort = false;
_latestDownloadBytes = 0;
_latestDownloadRealtime = Time.realtimeSinceStartup;
DownloadProgress = 0f;
DownloadedBytes = 0;
// 重置计时器
if (_tryAgainTimer > 0f)
YooLogger.Warning($"Try again download : {_requestURL}");
_tryAgainTimer = 0f;
}
/// <summary>
/// 检测请求超时
/// </summary>
protected void CheckRequestTimeout()
{
// 注意:在连续时间段内无新增下载数据及判定为超时
if (_isAbort == false)
{
if (_latestDownloadBytes != DownloadedBytes)
{
_latestDownloadBytes = DownloadedBytes;
_latestDownloadRealtime = UnityEngine.Time.realtimeSinceStartup;
}
float offset = UnityEngine.Time.realtimeSinceStartup - _latestDownloadRealtime;
if (offset > Options.Timeout)
{
YooLogger.Warning($"Download request timeout : {_requestURL}");
if (_webRequest != null)
_webRequest.Abort();
_isAbort = true;
}
}
}
/// <summary>
/// 检测请求结果
/// </summary>
protected bool CheckRequestResult()
{
HttpCode = _webRequest.responseCode;
#if UNITY_2020_3_OR_NEWER
if (_webRequest.result != UnityWebRequest.Result.Success)
{
Error = _webRequest.error;
return false;
}
else
{
return true;
}
#else
if (_webRequest.isNetworkError || _webRequest.isHttpError)
{
Error = _webRequest.error;
return false;
}
else
{
return true;
}
#endif
}
}
}

View File

@@ -2,12 +2,21 @@
namespace YooAsset namespace YooAsset
{ {
internal abstract class DownloadAssetBundleOperation : DefaultDownloadFileOperation internal abstract class DownloadAssetBundleOperation : AsyncOperationBase
{ {
internal DownloadAssetBundleOperation(PackageBundle bundle, DownloadFileOptions options) : base(bundle, options) /// <summary>
{ /// AssetBundle对象
} /// </summary>
public AssetBundle Result; public AssetBundle Result;
/// <summary>
/// 下载进度
/// </summary>
public float DownloadProgress { protected set; get; } = 0;
/// <summary>
/// 下载大小
/// </summary>
public long DownloadedBytes { protected set; get; } = 0;
} }
} }

View File

@@ -1,71 +0,0 @@

namespace YooAsset
{
internal class DownloadFileWrapper : FSDownloadFileOperation
{
private enum ESteps
{
None,
Download,
Done,
}
private readonly FSDownloadFileOperation _downloadFileOp;
private ESteps _steps = ESteps.None;
internal DownloadFileWrapper(FSDownloadFileOperation downloadFileOp) : base(downloadFileOp.Bundle)
{
_downloadFileOp = downloadFileOp;
}
internal override void InternalStart()
{
_steps = ESteps.Download;
}
internal override void InternalUpdate()
{
if (_steps == ESteps.None || _steps == ESteps.Done)
return;
if (_steps == ESteps.Download)
{
if (IsWaitForAsyncComplete)
_downloadFileOp.WaitForAsyncComplete();
if (_downloadFileOp.Status == EOperationStatus.None)
return;
_downloadFileOp.UpdateOperation();
Progress = _downloadFileOp.Progress;
DownloadedBytes = _downloadFileOp.DownloadedBytes;
DownloadProgress = _downloadFileOp.DownloadProgress;
if (_downloadFileOp.IsDone == false)
return;
_steps = ESteps.Done;
Status = _downloadFileOp.Status;
Error = _downloadFileOp.Error;
HttpCode = _downloadFileOp.HttpCode;
}
}
internal override void InternalWaitForAsyncComplete()
{
while (true)
{
if (ExecuteWhileDone())
{
_steps = ESteps.Done;
break;
}
}
}
public override void Release()
{
_downloadFileOp.Release();
}
public override void Reference()
{
_downloadFileOp.Reference();
}
}
}

View File

@@ -1,17 +1,33 @@
using UnityEngine; using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset namespace YooAsset
{ {
internal class DownloadWebEncryptAssetBundleOperation : DownloadAssetBundleOperation internal class DownloadEncryptAssetBundleOperation : DownloadAssetBundleOperation
{ {
protected enum ESteps
{
None,
CreateRequest,
CheckRequest,
TryAgain,
Done,
}
private UnityWebDataRequestOperation _unityWebDataRequestOp;
private readonly PackageBundle _bundle;
private readonly DownloadFileOptions _options;
private readonly bool _checkTimeout; private readonly bool _checkTimeout;
private readonly IWebDecryptionServices _decryptionServices; private readonly IWebDecryptionServices _decryptionServices;
private DownloadHandlerBuffer _downloadhandler;
protected int _requestCount = 0;
protected float _tryAgainTimer;
protected int _failedTryAgain;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
internal DownloadWebEncryptAssetBundleOperation(bool checkTimeout, IWebDecryptionServices decryptionServices, PackageBundle bundle, DownloadFileOptions options) : base(bundle, options) internal DownloadEncryptAssetBundleOperation(PackageBundle bundle, DownloadFileOptions options, bool checkTimeout, IWebDecryptionServices decryptionServices)
{ {
_bundle = bundle;
_options = options;
_checkTimeout = checkTimeout; _checkTimeout = checkTimeout;
_decryptionServices = decryptionServices; _decryptionServices = decryptionServices;
} }
@@ -27,118 +43,87 @@ namespace YooAsset
// 创建下载器 // 创建下载器
if (_steps == ESteps.CreateRequest) if (_steps == ESteps.CreateRequest)
{ {
// 获取请求地址 if (_decryptionServices == null)
_requestURL = GetRequestURL(); {
_steps = ESteps.Done;
// 重置变量 Status = EOperationStatus.Failed;
ResetRequestFiled(); Error = $"The {nameof(IWebDecryptionServices)} is null !";
YooLogger.Error(Error);
// 创建下载器 return;
CreateWebRequest(); }
string url = GetRequestURL();
_unityWebDataRequestOp = new UnityWebDataRequestOperation(url, _options.Timeout);
_unityWebDataRequestOp.StartOperation();
if (_checkTimeout == false)
_unityWebDataRequestOp.DontCheckTimeout();
_steps = ESteps.CheckRequest; _steps = ESteps.CheckRequest;
} }
// 检测下载结果 // 检测下载结果
if (_steps == ESteps.CheckRequest) if (_steps == ESteps.CheckRequest)
{ {
DownloadProgress = _webRequest.downloadProgress; _unityWebDataRequestOp.UpdateOperation();
DownloadedBytes = (long)_webRequest.downloadedBytes; Progress = _unityWebDataRequestOp.Progress;
Progress = DownloadProgress; DownloadProgress = _unityWebDataRequestOp.DownloadProgress;
if (_webRequest.isDone == false) DownloadedBytes = _unityWebDataRequestOp.DownloadedBytes;
{ if (_unityWebDataRequestOp.IsDone == false)
if (_checkTimeout)
CheckRequestTimeout();
return; return;
}
// 检查网络错误 // 检查网络错误
if (CheckRequestResult()) if (_unityWebDataRequestOp.Status == EOperationStatus.Succeed)
{ {
if (_decryptionServices == null) AssetBundle assetBundle = LoadEncryptedAssetBundle(_unityWebDataRequestOp.Result);
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"The {nameof(IWebDecryptionServices)} is null !";
YooLogger.Error(Error);
return;
}
var fileData = _downloadhandler.data;
if (fileData == null || fileData.Length == 0)
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = $"The download handler data is null or empty !";
YooLogger.Error(Error);
return;
}
AssetBundle assetBundle = LoadEncryptedAssetBundle(fileData);
if (assetBundle == null) if (assetBundle == null)
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
Status = EOperationStatus.Failed; Status = EOperationStatus.Failed;
Error = "Download handler asset bundle object is null !"; Error = "Failed load encrypted AssetBundle !";
} }
else else
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
Result = assetBundle;
Status = EOperationStatus.Succeed; Status = EOperationStatus.Succeed;
Result = assetBundle;
} }
} }
else else
{ {
_steps = ESteps.TryAgain; if (_failedTryAgain > 0)
{
_steps = ESteps.TryAgain;
YooLogger.Warning($"Failed download : {_unityWebDataRequestOp.URL} Try again !");
}
else
{
_steps = ESteps.Done;
Status = EOperationStatus.Failed;
Error = _unityWebDataRequestOp.Error;
YooLogger.Error(Error);
}
} }
// 注意:最终释放请求器
DisposeWebRequest();
} }
// 重新尝试下载 // 重新尝试下载
if (_steps == ESteps.TryAgain) if (_steps == ESteps.TryAgain)
{ {
if (FailedTryAgain <= 0)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
_tryAgainTimer += Time.unscaledDeltaTime; _tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f) if (_tryAgainTimer > 1f)
{ {
FailedTryAgain--; _tryAgainTimer = 0f;
_failedTryAgain--;
Progress = 0f;
DownloadProgress = 0f;
DownloadedBytes = 0;
_steps = ESteps.CreateRequest; _steps = ESteps.CreateRequest;
YooLogger.Warning(Error);
} }
} }
} }
internal override void InternalAbort() internal override void InternalAbort()
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
DisposeWebRequest(); if (_unityWebDataRequestOp != null)
} _unityWebDataRequestOp.AbortOperation();
private void CreateWebRequest()
{
_downloadhandler = new DownloadHandlerBuffer();
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL);
_webRequest.downloadHandler = _downloadhandler;
_webRequest.disposeDownloadHandlerOnDispose = true;
_webRequest.SendWebRequest();
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
} }
/// <summary> /// <summary>
@@ -147,11 +132,24 @@ namespace YooAsset
private AssetBundle LoadEncryptedAssetBundle(byte[] fileData) private AssetBundle LoadEncryptedAssetBundle(byte[] fileData)
{ {
var fileInfo = new WebDecryptFileInfo(); var fileInfo = new WebDecryptFileInfo();
fileInfo.BundleName = Bundle.BundleName; fileInfo.BundleName = _bundle.BundleName;
fileInfo.FileLoadCRC = Bundle.UnityCRC; fileInfo.FileLoadCRC = _bundle.UnityCRC;
fileInfo.FileData = fileData; fileInfo.FileData = fileData;
var decryptResult = _decryptionServices.LoadAssetBundle(fileInfo); var decryptResult = _decryptionServices.LoadAssetBundle(fileInfo);
return decryptResult.Result; return decryptResult.Result;
} }
/// <summary>
/// 获取网络请求地址
/// </summary>
protected string GetRequestURL()
{
// 轮流返回请求地址
_requestCount++;
if (_requestCount % 2 == 0)
return _options.FallbackURL;
else
return _options.MainURL;
}
} }
} }

View File

@@ -1,16 +1,33 @@
using UnityEngine; using UnityEngine;
using UnityEngine.Networking;
namespace YooAsset namespace YooAsset
{ {
internal class DownloadWebNormalAssetBundleOperation : DownloadAssetBundleOperation internal class DownloadNormalAssetBundleOperation : DownloadAssetBundleOperation
{ {
protected enum ESteps
{
None,
CreateRequest,
CheckRequest,
TryAgain,
Done,
}
private UnityAssetBundleRequestOperation _unityAssetBundleRequestOp;
private readonly PackageBundle _bundle;
private readonly DownloadFileOptions _options;
private readonly bool _disableUnityWebCache; private readonly bool _disableUnityWebCache;
private DownloadHandlerAssetBundle _downloadhandler;
protected int _requestCount = 0;
protected float _tryAgainTimer;
protected int _failedTryAgain;
private ESteps _steps = ESteps.None; private ESteps _steps = ESteps.None;
internal DownloadWebNormalAssetBundleOperation(bool disableUnityWebCache, PackageBundle bundle, DownloadFileOptions options) : base(bundle, options)
internal DownloadNormalAssetBundleOperation(PackageBundle bundle, DownloadFileOptions options, bool disableUnityWebCache)
{ {
_bundle = bundle;
_options = options;
_disableUnityWebCache = disableUnityWebCache; _disableUnityWebCache = disableUnityWebCache;
} }
internal override void InternalStart() internal override void InternalStart()
@@ -25,120 +42,78 @@ namespace YooAsset
// 创建下载器 // 创建下载器
if (_steps == ESteps.CreateRequest) if (_steps == ESteps.CreateRequest)
{ {
// 获取请求地址 string url = GetRequestURL();
_requestURL = GetRequestURL(); _unityAssetBundleRequestOp = new UnityAssetBundleRequestOperation(_bundle, _disableUnityWebCache, url, _options.Timeout);
_unityAssetBundleRequestOp.StartOperation();
// 重置变量
ResetRequestFiled();
// 创建下载器
CreateWebRequest();
_steps = ESteps.CheckRequest; _steps = ESteps.CheckRequest;
} }
// 检测下载结果 // 检测下载结果
if (_steps == ESteps.CheckRequest) if (_steps == ESteps.CheckRequest)
{ {
DownloadProgress = _webRequest.downloadProgress; _unityAssetBundleRequestOp.UpdateOperation();
DownloadedBytes = (long)_webRequest.downloadedBytes; Progress = _unityAssetBundleRequestOp.Progress;
Progress = DownloadProgress; DownloadedBytes = _unityAssetBundleRequestOp.DownloadedBytes;
if (_webRequest.isDone == false) DownloadProgress = _unityAssetBundleRequestOp.DownloadProgress;
{ if (_unityAssetBundleRequestOp.IsDone == false)
CheckRequestTimeout();
return; return;
}
// 检查网络错误 if (_unityAssetBundleRequestOp.Status == EOperationStatus.Succeed)
if (CheckRequestResult())
{ {
AssetBundle assetBundle = _downloadhandler.assetBundle; _steps = ESteps.Done;
if (assetBundle == null) Status = EOperationStatus.Succeed;
Result = _unityAssetBundleRequestOp.Result;
}
else
{
if (_failedTryAgain > 0)
{ {
_steps = ESteps.Done; _steps = ESteps.TryAgain;
Status = EOperationStatus.Failed; YooLogger.Warning($"Failed download : {_unityAssetBundleRequestOp.URL} Try again !");
Error = "Download handler asset bundle object is null !";
} }
else else
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
Result = assetBundle; Status = EOperationStatus.Failed;
Status = EOperationStatus.Succeed; Error = _unityAssetBundleRequestOp.Error;
YooLogger.Error(Error);
} }
} }
else
{
_steps = ESteps.TryAgain;
}
// 注意:最终释放请求器
DisposeWebRequest();
} }
// 重新尝试下载 // 重新尝试下载
if (_steps == ESteps.TryAgain) if (_steps == ESteps.TryAgain)
{ {
if (FailedTryAgain <= 0)
{
Status = EOperationStatus.Failed;
_steps = ESteps.Done;
YooLogger.Error(Error);
return;
}
_tryAgainTimer += Time.unscaledDeltaTime; _tryAgainTimer += Time.unscaledDeltaTime;
if (_tryAgainTimer > 1f) if (_tryAgainTimer > 1f)
{ {
FailedTryAgain--; _tryAgainTimer = 0f;
_failedTryAgain--;
Progress = 0f;
DownloadProgress = 0f;
DownloadedBytes = 0;
_steps = ESteps.CreateRequest; _steps = ESteps.CreateRequest;
YooLogger.Warning(Error);
} }
} }
} }
internal override void InternalAbort() internal override void InternalAbort()
{ {
_steps = ESteps.Done; _steps = ESteps.Done;
DisposeWebRequest(); if (_unityAssetBundleRequestOp != null)
_unityAssetBundleRequestOp.AbortOperation();
} }
private void CreateWebRequest() /// <summary>
/// 获取网络请求地址
/// </summary>
protected string GetRequestURL()
{ {
_downloadhandler = CreateWebDownloadHandler(); // 轮流返回请求地址
_webRequest = DownloadSystemHelper.NewUnityWebRequestGet(_requestURL); _requestCount++;
_webRequest.downloadHandler = _downloadhandler; if (_requestCount % 2 == 0)
_webRequest.disposeDownloadHandlerOnDispose = true; return _options.FallbackURL;
_webRequest.SendWebRequest();
}
private void DisposeWebRequest()
{
if (_webRequest != null)
{
//注意引擎底层会自动调用Abort方法
_webRequest.Dispose();
_webRequest = null;
}
}
private DownloadHandlerAssetBundle CreateWebDownloadHandler()
{
if (_disableUnityWebCache)
{
var downloadhandler = new DownloadHandlerAssetBundle(_requestURL, Bundle.UnityCRC);
#if UNITY_2020_3_OR_NEWER
downloadhandler.autoLoadAssetBundle = false;
#endif
return downloadhandler;
}
else else
{ return _options.MainURL;
// 注意:优先从浏览器缓存里获取文件
// The file hash defining the version of the asset bundle.
Hash128 fileHash = Hash128.Parse(Bundle.FileHash);
var downloadhandler = new DownloadHandlerAssetBundle(_requestURL, fileHash, Bundle.UnityCRC);
#if UNITY_2020_3_OR_NEWER
downloadhandler.autoLoadAssetBundle = false;
#endif
return downloadhandler;
}
} }
} }
} }

View File

@@ -366,7 +366,7 @@ namespace YooAsset
foreach (var downloader in _downloaders) foreach (var downloader in _downloaders)
{ {
downloader.Release(); downloader.AbortOperation();
} }
} }
} }