300 lines
9.3 KiB
C#
300 lines
9.3 KiB
C#
using System.IO;
|
|
using UnityEngine;
|
|
using UnityEngine.Networking;
|
|
|
|
namespace NBC.Asset
|
|
{
|
|
/// <summary>
|
|
/// 下载文件
|
|
/// </summary>
|
|
public class DownloadFileTask : DownloadTaskBase
|
|
{
|
|
private const string DownloadHeaderKey = "Content-Length";
|
|
private const int RetryDownloadCount = 3;
|
|
|
|
public readonly string DownloadPath;
|
|
|
|
public readonly string SavePath;
|
|
public readonly string FileHash;
|
|
|
|
/// <summary>
|
|
/// 开启断点续传
|
|
/// </summary>
|
|
public bool ReDownload = true;
|
|
|
|
public DownloadFileTask(string path, string savePath, string hash = "")
|
|
{
|
|
DownloadPath = path;
|
|
SavePath = savePath;
|
|
FileHash = hash;
|
|
}
|
|
|
|
public enum DownLoadStatus
|
|
{
|
|
None,
|
|
GetHeader,
|
|
PrepareDownload,
|
|
Download,
|
|
VerifyingFile,
|
|
Success,
|
|
Failed,
|
|
}
|
|
|
|
private ulong _downloadTotalSize = 1;
|
|
private UnityWebRequest _content;
|
|
private UnityWebRequest _header;
|
|
private bool _isAbort;
|
|
private ulong _latestDownloadBytes;
|
|
private float _latestDownloadRealtime;
|
|
private ulong _fileOriginLength;
|
|
private int RetryCount;
|
|
|
|
private long ResponseCode = 0;
|
|
|
|
public DownLoadStatus DownloadStatus { get; protected internal set; } = DownLoadStatus.None;
|
|
public override float Progress => DownloadedBytes * 1f / DownloadTotalSize;
|
|
|
|
public ulong DownloadedBytes { get; protected set; }
|
|
|
|
public ulong DownloadTotalSize
|
|
{
|
|
get => _downloadTotalSize;
|
|
set
|
|
{
|
|
_downloadTotalSize = value;
|
|
if (_downloadTotalSize < 1) _downloadTotalSize = 1;
|
|
}
|
|
}
|
|
|
|
|
|
public void Abort()
|
|
{
|
|
Fail("abort");
|
|
Dispose();
|
|
}
|
|
|
|
protected override void OnStart()
|
|
{
|
|
DownloadStatus = DownLoadStatus.GetHeader;
|
|
}
|
|
|
|
protected override NTaskStatus OnProcess()
|
|
{
|
|
if (DownloadStatus == DownLoadStatus.GetHeader)
|
|
{
|
|
_header = UnityWebRequest.Head(DownloadPath);
|
|
_header.SendWebRequest();
|
|
DownloadStatus = DownLoadStatus.PrepareDownload;
|
|
}
|
|
|
|
if (DownloadStatus == DownLoadStatus.PrepareDownload)
|
|
{
|
|
if (_header == null)
|
|
{
|
|
Fail($"get header info error");
|
|
Debug.LogError("get header info error");
|
|
return NTaskStatus.Fail;
|
|
}
|
|
|
|
if (!_header.isDone) return NTaskStatus.Running;
|
|
|
|
Reset();
|
|
//远程文件信息
|
|
var value = _header.GetResponseHeader(DownloadHeaderKey);
|
|
if (ulong.TryParse(value, out var totalSize))
|
|
{
|
|
DownloadTotalSize = totalSize;
|
|
}
|
|
|
|
if (ReDownload)
|
|
{
|
|
//读取未下载完成的文件信息
|
|
var tempInfo = new FileInfo(SavePath);
|
|
if (tempInfo.Exists)
|
|
{
|
|
_fileOriginLength = (ulong)tempInfo.Length;
|
|
if (_fileOriginLength == DownloadTotalSize)
|
|
{
|
|
DownloadedBytes = _fileOriginLength;
|
|
DownloadStatus = DownLoadStatus.VerifyingFile;
|
|
return NTaskStatus.Running;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_fileOriginLength = 0;
|
|
}
|
|
|
|
_content = UnityWebRequest.Get(DownloadPath);
|
|
if (_fileOriginLength > 0)
|
|
{
|
|
Debug.Log($"断点续传===={_fileOriginLength} path={DownloadPath}");
|
|
#if UNITY_2019_1_OR_NEWER
|
|
_content.SetRequestHeader("Range", $"bytes={_fileOriginLength}-");
|
|
_content.downloadHandler = new DownloadHandlerFile(SavePath, true);
|
|
#else
|
|
_request.DownloadedBytes = 0;
|
|
_content.downloadHandler = new DownloadHandlerFile(TempPath);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
_content.downloadHandler = new DownloadHandlerFile(SavePath);
|
|
}
|
|
|
|
_content.certificateHandler = new DownloadCertificateHandler();
|
|
_content.disposeDownloadHandlerOnDispose = true;
|
|
_content.disposeCertificateHandlerOnDispose = true;
|
|
_content.disposeUploadHandlerOnDispose = true;
|
|
_content.SendWebRequest();
|
|
DownloadStatus = DownLoadStatus.Download;
|
|
}
|
|
|
|
if (DownloadStatus == DownLoadStatus.Download)
|
|
{
|
|
DownloadedBytes = _fileOriginLength + _content.downloadedBytes;
|
|
if (!_content.isDone)
|
|
{
|
|
CheckTimeout();
|
|
return NTaskStatus.Running;
|
|
}
|
|
|
|
bool hasError = false;
|
|
// 检查网络错误
|
|
#if UNITY_2020_3_OR_NEWER
|
|
if (_content.result != UnityWebRequest.Result.Success)
|
|
{
|
|
hasError = true;
|
|
_errorMsg = _content.error;
|
|
ResponseCode = _content.responseCode;
|
|
}
|
|
#else
|
|
if (_content.isNetworkError || _content.isHttpError)
|
|
{
|
|
hasError = true;
|
|
_errorMsg = _content.error;
|
|
ResponseCode = _content.responseCode;
|
|
}
|
|
#endif
|
|
// 如果网络异常
|
|
if (hasError)
|
|
{
|
|
RetryCount++;
|
|
if (RetryCount <= RetryDownloadCount)
|
|
{
|
|
Debug.Log($"网络异常 重新开始下载={DownloadPath} code={ResponseCode} msg={_content.error}");
|
|
//重新开始下载
|
|
DownloadStatus = DownLoadStatus.PrepareDownload;
|
|
}
|
|
else
|
|
{
|
|
//重试后还是网络错误,直接失败
|
|
Debug.Log("重试后还是网络错误,直接失败");
|
|
DownloadStatus = DownLoadStatus.Failed;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DownloadStatus = DownLoadStatus.VerifyingFile;
|
|
}
|
|
}
|
|
|
|
if (DownloadStatus == DownLoadStatus.VerifyingFile)
|
|
{
|
|
Dispose();
|
|
var tryPass = false;
|
|
var tempInfo = new FileInfo(SavePath);
|
|
|
|
if (tempInfo.Exists)
|
|
{
|
|
if (tempInfo.Length == (long)DownloadTotalSize)
|
|
{
|
|
tryPass = true;
|
|
}
|
|
else
|
|
{
|
|
_errorMsg = "file size error";
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(FileHash))
|
|
{
|
|
var hash = Util.ComputeHash(SavePath);
|
|
if (FileHash.Equals(hash))
|
|
{
|
|
tryPass = true;
|
|
}
|
|
else
|
|
{
|
|
_errorMsg = "file hash error";
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_errorMsg = "file not exists";
|
|
}
|
|
|
|
if (!tryPass)
|
|
{
|
|
// 验证失败后删除文件
|
|
if (File.Exists(SavePath))
|
|
File.Delete(SavePath);
|
|
Debug.Log("验证失败后删除文件,尝试重新下载");
|
|
//重新下载
|
|
DownloadStatus = DownLoadStatus.PrepareDownload;
|
|
}
|
|
else
|
|
{
|
|
DownloadStatus = DownLoadStatus.Success;
|
|
}
|
|
}
|
|
|
|
if (DownloadStatus == DownLoadStatus.Success)
|
|
{
|
|
return NTaskStatus.Success;
|
|
}
|
|
|
|
if (DownloadStatus == DownLoadStatus.Failed)
|
|
{
|
|
return NTaskStatus.Fail;
|
|
}
|
|
|
|
return NTaskStatus.Running;
|
|
}
|
|
|
|
|
|
private void CheckTimeout()
|
|
{
|
|
if (_isAbort) return;
|
|
|
|
if (_latestDownloadBytes != DownloadedBytes)
|
|
{
|
|
_latestDownloadBytes = DownloadedBytes;
|
|
_latestDownloadRealtime = Time.realtimeSinceStartup;
|
|
}
|
|
|
|
float offset = Time.realtimeSinceStartup - _latestDownloadRealtime;
|
|
if (_latestDownloadRealtime > 0 && offset > Const.DownloadTimeOut)
|
|
{
|
|
_content.Abort();
|
|
_isAbort = true;
|
|
}
|
|
}
|
|
|
|
private void Dispose()
|
|
{
|
|
if (_header != null)
|
|
{
|
|
_header.Dispose();
|
|
_header = null;
|
|
}
|
|
|
|
if (_content != null)
|
|
{
|
|
_content.Dispose();
|
|
_content = null;
|
|
}
|
|
}
|
|
}
|
|
} |