Files
Fishing2/Assets/Scripts/NBC/Asset/Runtime/Tasks/Download/DownloadFileTask.cs

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;
}
}
}
}