Unity Http下载文件断点续传

下载一个文件中断时,下次启动继续沿着上一次下载在很多游戏的更新中会用到

断点续传的核心代码API:

HttpWebRequest request = WebRequest.Create(tNowDownloadInfo.nNowDownloadURL) as HttpWebRequest;
request.ProtocolVersion = HttpVersion.Version10;
request.ServicePoint.ConnectionLimit = int.MaxValue;
//使用流操作文件
FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
//获取文件现在的长度
long tmpLoaclFileLength = fs.Length;
//获取下载文件的总长度
long tmpTotlaSize = GetLength(tNowDownloadInfo.nNowDownloadURL, tNowDownloadInfo.nDownloadTimeout);
//断点续传重要API,设置本地开始下载起始位置
fs.Seek(tmpLoaclFileLength, SeekOrigin.Begin);

如果使用带https头的则需要添加设置协议:

if (tNowDownloadInfo.nNowDownloadURL.StartsWith("https", StringComparison.OrdinalIgnoreCase))
{
    ServicePointManager.ServerCertificateValidationCallback = new                             
    RemoteCertificateValidationCallback(ValidateServerCertificate);
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
}
else
{
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
}



static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
   return true; //接受     
}

完整代码:

using UnityEngine;
using System.Collections;
using System.Threading;
using System.IO;
using System.Net;
using System;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Collections.Generic;

public enum DownloadResult
{
    /// <summary>
    /// 当前下载的某一个文件已下载成功,不是指一个完全的下载任务已完成
    /// </summary>
    Success,
    /// <summary>
    /// 某一个下载文件下载失败
    /// </summary>
    Fail,
    /// <summary>
    /// 获取不到文件
    /// </summary>
    NotFindFile,
    /// <summary>
    /// 下载文件中
    /// </summary>
    Downloading,
    /// <summary>
    /// 完成一个下载任务
    /// </summary>
    AllDownloadFinished,
}

public class DownloadInfo
{
    /// <summary>
    /// 是否可以执行回调,用于解决线程之间的问题
    /// </summary>
    public volatile bool tIsCanCallback;
    /// <summary>
    /// 当前下载的任务文件大小总和
    /// </summary>
    public int nAllFileTotalSize = 1;
    public DownloadResult tDownloadState;
    public float tProgress = 0;
    public bool tIsDone = false;
    public DownlaodFileCallback tDownloadCallback;
    public string[] tSavaeFullFilePath;
    public string[] tDownlaodURL;
    /// <summary>
    /// 当前正在下载的文件地址
    /// </summary>
    public string nNowDownloadURL = "";
    /// <summary>
    /// 当前下载完成保存本地的地址
    /// </summary>
    public string nSavaeFullFilePath = "";
    /// <summary>
    /// 当前下载索引
    /// </summary>
    public int nDownloadIndex = 0;
    /// <summary>
    /// 每秒的下载速度
    /// </summary>
    public int nTimeDownSpeed = 0;
    /// <summary>
    /// 每隔多少时间执行回调一次
    /// </summary>
    public float nUpdateExeCallTime = 1f;
    /// <summary>
    /// 当前总下载的进度
    /// </summary>
    public long nLocalFileTotalSize = 0;
    /// <summary>
    /// 当前下载的文件总大小
    /// </summary>
    public int nNowTotalSize = 0;

    public int nDownloadTimeout = 6000;
    /// <summary>
    /// 下载的数据是否保存到本地,如果需要设置的话请保证访问的文件小于2MB
    /// </summary>
    public bool nIsSavaeFile = true;
    /// <summary>
    /// 下载的数据,只有当nIsSavaeFile false的时候数据会存在该字段中
    /// </summary>
    public byte[] nToFileData;
}

public delegate void DownlaodFileCallback(DownloadInfo varInfo, DownloadResult varResultType);
/// <summary>
/// 通过http下载资源
/// </summary>
public class HttpDownload : MonoBehaviour
{
    static HttpDownload Instance;
    public static HttpDownload GetInstacne()
    {
        if (Instance == null)
        {
            GameObject tHttpDownLoad = new GameObject("HttpDownLoad");
            Instance = tHttpDownLoad.AddComponent<HttpDownload>();
            GameObject.DontDestroyOnLoad(tHttpDownLoad);
        }
        return Instance;
    }
    static readonly object LockObject = new object();

    Thread tDownloadThread;
    Queue<DownloadInfo> tDownloadList = new Queue<DownloadInfo>();
    static DownloadInfo tNowDownloadInfo;

    volatile bool tIsKillGame = false;
    /// <summary>
    /// 下载方法(断点续传)
    /// </summary>
    /// <param name="url">URL下载地址</param>
    /// <param name="savePath">Save path保存路径</param>
    /// <param name="callBack">Call back回调函数</param>
    public void DownLoad(DownloadInfo varInfo)
    {
        tDownloadList.Enqueue(varInfo);
        StartTask();
    }

    public void UpgradeVeresionFiles(DownloadInfo varInfo)
    {
        for (int i = 0; i < varInfo.tSavaeFullFilePath.Length; i++)
        {
            if (File.Exists(varInfo.tSavaeFullFilePath[i]))
            {
                File.Delete(varInfo.tSavaeFullFilePath[i]);
            }
        }
        DownLoad(varInfo);
    }

    void StartTask()
    {
        if (tNowDownloadInfo == null)
        {
            tDownloadThread = new Thread(DownloadTask);
            //开启子线程
            tDownloadThread.IsBackground = true;
            tDownloadThread.Start();
        }
    }

    void DownloadTask()
    {
        while (true)
        {
            Thread.Sleep(1000);//执行网络下载之前先暂停1秒
            if (tDownloadList.Count > 0)
            {
                tNowDownloadInfo = tDownloadList.Dequeue();
                lock (LockObject)
                {
                    if (!tNowDownloadInfo.nIsSavaeFile)
                    {
                        Stream tmpDownStream = null;
                        try
                        {
                            HttpWebRequest request = WebRequest.Create(tNowDownloadInfo.nNowDownloadURL) as HttpWebRequest;
                            request.ProtocolVersion = HttpVersion.Version10;
                            request.ServicePoint.ConnectionLimit = int.MaxValue;
                            request.Timeout = tNowDownloadInfo.nDownloadTimeout;

                            if (tNowDownloadInfo.nNowDownloadURL.StartsWith("https", StringComparison.OrdinalIgnoreCase))
                            {
                                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
                                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
                            }
                            else
                            {
                                ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
                            }
                            tmpDownStream = request.GetResponse().GetResponseStream();
                            byte[] buffer = new byte[1024 * 2];
                            int tmpReadLength = tmpDownStream.Read(buffer, 0, buffer.Length);
                            List<byte> tmpCopyArray = new List<byte>();
                            while (tmpReadLength > 0)
                            {
                                byte[] tmp = new byte[tmpReadLength];
                                Array.Copy(buffer, tmp, tmpReadLength);
                                tmpCopyArray.AddRange(tmp);
                                tmpReadLength = tmpDownStream.Read(buffer, 0, buffer.Length);
                            }
                            tNowDownloadInfo.nToFileData = tmpCopyArray.ToArray();

                            if (tNowDownloadInfo.tDownloadCallback != null)
                            {
                                tNowDownloadInfo.tDownloadState = DownloadResult.Success;
                                tNowDownloadInfo.nDownloadIndex = 0;
                                tNowDownloadInfo.tIsCanCallback = true;
                                request = null;
                            }
                        }
                        catch (Exception e)
                        {
                            if (tNowDownloadInfo.tDownloadCallback != null)
                            {
                                tNowDownloadInfo.tDownloadState = DownloadResult.Fail;
                                tNowDownloadInfo.nDownloadIndex = 0;
                                tNowDownloadInfo.tIsCanCallback = true;
                            }
                            tmpDownStream.Close();
                            tmpDownStream.Dispose();
                            //Debug.LogError("Error:" + e.ToString());
                        }
                        finally
                        {
                            tmpDownStream.Close();
                            tmpDownStream.Dispose();
                        }
                    }
                    else
                    {
                        for (int i = 0; i < tNowDownloadInfo.tSavaeFullFilePath.Length; i++)
                        {
                            tNowDownloadInfo.nNowDownloadURL = tNowDownloadInfo.tDownlaodURL[i];
                            tNowDownloadInfo.nSavaeFullFilePath = tNowDownloadInfo.tSavaeFullFilePath[i];
                            if (tNowDownloadInfo.nNowDownloadURL.StartsWith("https", StringComparison.OrdinalIgnoreCase))
                            {
                                ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
                                ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
                            }
                            else
                            {
                                ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
                            }

                            string tmpDir = Path.GetDirectoryName(tNowDownloadInfo.nSavaeFullFilePath);
                            //判断保存路径是否存在
                            if (!Directory.Exists(tmpDir))
                            {
                                Directory.CreateDirectory(tmpDir);
                            }

                            //这是要下载的文件名,比如从服务器下载a.zip到D盘,保存的文件名是test
                            string filePath = tNowDownloadInfo.nSavaeFullFilePath;

                            //使用流操作文件
                            FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate, FileAccess.Write);
                            //获取文件现在的长度
                            long tmpLoaclFileLength = fs.Length;
                            //获取下载文件的总长度
                            long tmpTotlaSize = GetLength(tNowDownloadInfo.nNowDownloadURL, tNowDownloadInfo.nDownloadTimeout);
                            if (tmpTotlaSize == 0)
                            {
                                tNowDownloadInfo.tDownloadState = DownloadResult.NotFindFile;
                                tNowDownloadInfo.nDownloadIndex = i;
                                tNowDownloadInfo.tIsCanCallback = true;
                                if (fs != null)
                                {
                                    fs.Close();
                                    fs.Dispose();
                                }
                                continue;
                            }
                            tNowDownloadInfo.nNowTotalSize = (int)tmpTotlaSize;

                            float tmpProgress = 0;
                            //如果没下载完
                            if (tmpLoaclFileLength < tmpTotlaSize)
                            {
                                //断点续传重要API,设置本地开始下载起始位置
                                fs.Seek(tmpLoaclFileLength, SeekOrigin.Begin);

                                HttpWebRequest request = WebRequest.Create(tNowDownloadInfo.nNowDownloadURL) as HttpWebRequest;
                                request.ProtocolVersion = HttpVersion.Version10;
                                request.ServicePoint.ConnectionLimit = int.MaxValue;
                                request.Timeout = tNowDownloadInfo.nDownloadTimeout;
                                Stream tmpDownStream = null;
                                try
                                {

                                    //断点续传核心,设置远程访问文件流的起始位置
                                    request.AddRange(tmpLoaclFileLength,tmpTotlaSize);
                                    tmpDownStream = request.GetResponse().GetResponseStream();
                                    byte[] buffer = new byte[1024 * 1024 * 3];
                                    //使用流读取内容到buffer中
                                    //方法返回值代表读取的实际长度,并不是buffer有多大,stream就会读进去多少
                                    int tmpReadLength = tmpDownStream.Read(buffer, 0, buffer.Length);


                                    float time = 0;
                                    int tmpDownloadSpeed = 0;
                                    while (tmpReadLength > 0)
                                    {
                                        //如果Unity客户端关闭,停止下载
                                        if (tIsKillGame)
                                        {
                                            tmpDownStream.Close();
                                            tmpDownStream.Dispose();
                                            fs.Close();
                                            fs.Dispose();
                                            return;
                                        }
                                        //将内容再写入本地文件中
                                        fs.Write(buffer, 0, tmpReadLength);
                                        //计算进度
                                        tmpLoaclFileLength += tmpReadLength;

                                        tmpProgress = (float)tmpLoaclFileLength / tmpTotlaSize;
                                        //类似尾递归
                                        tmpReadLength = tmpDownStream.Read(buffer, 0, buffer.Length);

                                        tNowDownloadInfo.nLocalFileTotalSize = tmpLoaclFileLength;
                                        time += Main.deltaTime;
                                        Main.deltaTime = 0;
                                        if (time >= tNowDownloadInfo.nUpdateExeCallTime)
                                        {
                                            tNowDownloadInfo.nTimeDownSpeed = tmpDownloadSpeed;
                                            tNowDownloadInfo.tDownloadState = DownloadResult.Downloading;
                                            tNowDownloadInfo.nDownloadIndex = i;
                                            tNowDownloadInfo.tIsCanCallback = true;
                                            time = 0;
                                            tmpDownloadSpeed = 0;
                                        }
                                        tmpDownloadSpeed += tmpReadLength;
                                    }
                                    if (tmpDownStream != null)
                                    {
                                        tmpDownStream.Close();
                                        tmpDownStream.Dispose();
                                    }

                                }
                                catch (Exception e)
                                {
                                    //Debug.Log(e.ToString());
                                    tNowDownloadInfo.tDownloadState = DownloadResult.Fail;
                                    tNowDownloadInfo.nDownloadIndex = i;
                                    tNowDownloadInfo.tIsCanCallback = true;
                                    if (fs != null)
                                    {
                                        fs.Close();
                                        fs.Dispose();
                                    }
                                    continue;
                                }
                                finally
                                {
                                    fs.Close();
                                    fs.Dispose();
                                    if (tmpDownStream != null)
                                    {
                                        tmpDownStream.Close();
                                        tmpDownStream.Dispose();
                                    }
                                }

                            }
                            else
                            {
                                tmpProgress = 1;
                            }
                            fs.Close();
                            fs.Dispose();
                            //如果下载完毕,执行回调
                            if (tmpProgress == 1)
                            {
                                if (i == tNowDownloadInfo.tDownlaodURL.Length - 1)
                                {
                                    tNowDownloadInfo.tIsDone = true;
                                    tNowDownloadInfo.tDownloadState = DownloadResult.AllDownloadFinished;
                                }
                                else
                                {
                                    tNowDownloadInfo.tDownloadState = DownloadResult.Success;
                                }
                                tNowDownloadInfo.nDownloadIndex = i;
                                tNowDownloadInfo.tIsCanCallback = true;
                            }
                        }
                    }
                }
            }
            Thread.Sleep(500);
        }
    }
    /// <summary>
    /// 检查回调
    /// </summary>
    void Update()
    {
        if (tNowDownloadInfo != null && tNowDownloadInfo.tIsCanCallback)
        {
            if (tNowDownloadInfo.tDownloadCallback != null)
            {
                try
                {
                    tNowDownloadInfo.tIsCanCallback = false;
                    tNowDownloadInfo.tDownloadCallback(tNowDownloadInfo, tNowDownloadInfo.tDownloadState);
                }
                catch (Exception e)
                {
                    tNowDownloadInfo.tIsCanCallback = false;
                }
            }
        }
    }

    static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
    {
        return true; //总是接受     
    }
    /// <summary>
    /// 获取下载文件的大小
    /// </summary>
    /// <returns>The length.</returns>
    /// <param name="url">URL.</param>
    long GetLength(string url, int timeout)
    {
        try
        {
            HttpWebRequest requet = HttpWebRequest.Create(url) as HttpWebRequest;
            requet.Method = "HEAD";
            requet.Timeout = timeout;
            HttpWebResponse response = requet.GetResponse() as HttpWebResponse;
            long length = response.ContentLength;
            response.Close();
            return length;
        }
        catch (Exception)
        {
            return 0;
        }
    }
    void OnApplicationQuit()
    {
        tIsKillGame = true;
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值