DownloadHandler 断点续传工具类

看到别人封装好的一个下载工具类,支持断点续传,很好用,记录一下。

原文地址


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityTools;

public class Downloader
{
    private string url = null; // 需要下载的文件的地址
    private string savePath = null; // 保存的路径
    private UnityWebRequest request = null; // Unity中用来与Web服务器进行通信的类
    private UnityTools.DownloadHandler downloadHandler = null; // 我们自己实现的下载处理类
    private ErrorEventHandler onError = null; // 出错回调
    private CompletedEventHandler onCompleted = null; // 完成回调
    private ProgressEventHandler onProgress = null; // 进度回调

    public Downloader(string url, string savePath, CompletedEventHandler onCompleted, ProgressEventHandler onProgress,
        ErrorEventHandler onError)
    {
        this.url = url;
        this.savePath = savePath;
        this.onCompleted = onCompleted;
        this.onProgress = onProgress;
        this.onError = onError;
    }

    /// 
    /// 开始下载
    /// 
    /// 超时时间(秒)
    public void Start(int timeout = 600)
    {
        request = UnityWebRequest.Get(url);
        if (!string.IsNullOrEmpty(savePath))
        {
            request.timeout = timeout;
            request.disposeDownloadHandlerOnDispose = true;
            downloadHandler = new UnityTools.DownloadHandler(savePath, onCompleted, onProgress, onError);
            // 这里是设置http的请求头
            // range表示请求资源的部分内容(不包括响应头的大小),单位是byte
            request.SetRequestHeader("range", $"bytes={downloadHandler.CurrLength}-");
            request.downloadHandler = downloadHandler;
        }
        request.SendWebRequest();
    }

    /// 
    /// 清理
    /// 
    public void Dispose()
    {
        onError = null;
        onCompleted = null;
        onProgress = null;
        if (request != null)
        {
            // 如果下载没有完成,就中止
            if (!request.isDone)
                request.Abort();
            request.Dispose();
            request = null;
        }
    }
}

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.Networking;

namespace UnityTools
{
    public enum ErrorCode
    {
        DownloadContentEmpth,//需要下载的文件内容为空
        TempFileMissing,//临时文件丢失
    }

    /// <summary>
    /// 下载出错
    /// </summary>
    /// <param name="errorCode">错误码</param>
    /// <param name="message">错误信息</param>
    public delegate void ErrorEventHandler(ErrorCode errorCode, string message);

    /// <summary>
    /// 下载完成
    /// </summary>
    /// <param name="message">下载进度</param>
    public delegate void CompletedEventHandler(string message);

    /// <summary>
    /// 下载进度
    /// </summary>
    /// <param name="prg">当前进度</param>
    /// <param name="currLength">当前下载完成的长度</param>
    /// <param name="totalLength">文件总长度</param>
    public delegate void ProgressEventHandler(float prg, long currLength, long totalLength);


    public class DownloadHandler : DownloadHandlerScript
    {
        private string savePath = null;//保存到的路径
        private string tempPath = null;//下载临时文件路径
        private long currLength = 0;//当前已经下载的数据长度
        private long totalLength = 0; // 文件总数据长度
        private long contentLength = 0; // 本次需要下载的数据长度
        private FileStream fileStream = null; // 文件流,用来将接收到的数据写入文件
        private ErrorEventHandler onError = null; // 出错回调
        private CompletedEventHandler onCompleted = null; // 完成回调
        private ProgressEventHandler onProgress = null; // 进度回调

        public DownloadHandler(string savePath, CompletedEventHandler onCompleted, ProgressEventHandler onProgress, ErrorEventHandler onError)
        {
            this.savePath = savePath;
            this.onCompleted = onCompleted;
            this.onProgress = onProgress;
            this.onError = onError;
            this.tempPath = savePath + ".temp";
            fileStream = new FileStream(tempPath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
            currLength = fileStream.Length;
            fileStream.Position = currLength;
        }
        public long CurrLength
        {
            get { return currLength; }
        }

        public long TotalLength
        {
            get { return totalLength; }
        }

        /// <summary>
        /// 在收到Content-Length标头调用的回调
        /// </summary>
        /// <param name="contentLength"></param>
        protected override void ReceiveContentLengthHeader(ulong contentLength)
        {
            this.contentLength = (long)contentLength;
            totalLength = this.contentLength + currLength;
        }
        /// <summary>
        /// 从远程服务器收到数据时的回调
        /// </summary>
        /// <param name="data"></param>
        /// <param name="dataLength"></param>
        /// <returns></returns>
        protected override bool ReceiveData(byte[] data, int dataLength)
        {
            //如果下载的数据长度小于等于0 就结束下载
            if (contentLength <= 0 || data == null || data.Length <= 0)
            {
                return false;
            }
            fileStream.Write(data, 0, dataLength);
            currLength += dataLength;
            onProgress?.Invoke(currLength * 1.0f / totalLength, CurrLength, totalLength);
            return true;
        }
        protected override void CompleteContent()
        {
            //接收完所有数据后,首先关闭文件流
            Close();
            //如果服务器上不存在该文件,请求下载的内容长度会为0
            //所以需要特殊处理这种情况
            if (contentLength <= 0)
            {
                onError(ErrorCode.DownloadContentEmpth, "下载内容长度为0");
                return;
            }
            //如果下载完成后,临时问价你如果被意外删除了,也抛出错误提示
            if (!File.Exists(tempPath))
            {
                onError(ErrorCode.TempFileMissing, "下载临时缓冲文件丢失");
                return;
            }
            //如果下载的文件已经存在,就删除原文件
            if (File.Exists(savePath))
            {
                File.Delete(savePath);
            }
            //通过以上的校验后,就将临时文件移动到目标路径下,下载成功
            File.Move(tempPath, savePath);
            onCompleted("下载文件完成");
        }

        public void Close()
        {
            if (fileStream == null) return;
            fileStream.Close();
            fileStream.Dispose();
            fileStream = null;
        }
    }


}


使用方法:

//需要下载的地方这么使用
Downloader downloader = new Downloader(url, path, OnCompleted, OnProgress, OnError);
downloader.Start();

//以下是回调方法
private void OnProgress(float prg, long currLength, long totalLength)
{
    Debug.LogFormat("下载进度{0:0.00}%,{1}M/{2}M", (prg * 100), currLength * 1.0f / 1024 / 1024,
            totalLength * 1.0f / 1024 / 1024);
}

private void OnCompleted(string msg)
{
    Debug.Log(msg);
}

private void OnError(ErrorCode code, string msg)
{

}