unity 实现断点续传

  1. 在下载文件的时候我们会先创建一个与下载文件对应的以.temp为后缀的临时文件, 下载的文件数据会写入这个临时文件中
  2. 每次开始下载的时候会检查是否存在需下载文件的临时文件,如果存在,便从该文件数据长度的地方开始下载写入
  3. 下载完成后便将临时文件移动到目标下载目录

下面是脚本内容

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

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


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

public class Downloader 
{
    private string url = null; // 需要下载的文件的地址
    private string savePath = null; // 保存的路径
    private UnityWebRequest request = null; // Unity中用来与Web服务器进行通信的类
    private 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 = 10)
    {
        request = UnityWebRequest.Get(url);
        if (!string.IsNullOrEmpty(savePath))
        {
            request.timeout = timeout;
            request.disposeDownloadHandlerOnDispose = true;
            downloadHandler = new 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 UnityEngine;

public class Example : MonoBehaviour
{
  
        private Downloader downloader;
        private string url = "http://vfx.mtime.cn/Video/2019/03/21/mp4/190321153853126488.mp4";
        private string path;

        private void Start()
        {
            path = Application.dataPath + "/../190321153853126488.mp4";
        }
    //按A键开始下载   按D键结束下载
        private void Update()
        {
            if (Input.GetKeyDown(KeyCode.A))
            {
                if (downloader != null)
                {
                    return;
                }
                else
                {
                    downloader = new Downloader(url, path, OnCompleted, OnProgress, OnError);
                    downloader.Start();
                }
            }

            if (Input.GetKeyDown(KeyCode.D))
            {
                if (downloader != null)
                {
                    downloader.Dispose();
                    downloader = null;
                }
            }
        }

        private void OnApplicationQuit()
        {
            if (downloader != null)
            {
                downloader.Dispose();
                downloader = null;
            }
        }

        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)
        {
        }
  }

转载地址

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值