Unity断点续传

一.使用 Http 显示断点下载

using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using UnityEngine;

/// <summary>
/// 断点续传下载器
/// </summary>
public class BreakpointDownloader
{
    
    
    /// <summary>
    /// 下载【需要开线程 否则会卡主线程】
    /// </summary>
    public static void download(string downUrl, string writeFilePath, Action<string> errAction, Action<double, bool> callBack)
    {
    
    
        long startPos = 0;//打开上次下载的文件
        FileStream fs = null;
        if (File.Exists(writeFilePath))
        {
    
    
            fs = new FileStream(writeFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
            startPos = fs.Length;
            fs.Seek(startPos, SeekOrigin.Current); //移动文件流中的当前指针
        }
        else
        {
    
    
            string direName = Path.GetDirectoryName(writeFilePath);
            if (!Directory.Exists(direName))
                Directory.CreateDirectory(direName);
            fs = new FileStream(writeFilePath, FileMode.Create);
        }

        // 下载逻辑
        HttpWebRequest request = null;
        WebResponse respone = null;
        Stream ns = null;
        try
        {
    
    
            ServicePointManager.ServerCertificateValidationCallback = MyRemoteCertificateValidationCallback;

            request = WebRequest.Create(downUrl) as HttpWebRequest;
            request.ReadWriteTimeout = 10000;
            request.Timeout = 10000;
            if (startPos > 0)
                request.AddRange((int)startPos);  //设置Range值,断点续传【支持2G以下的分片 AddRange方法只接受Int32型的参数】 Range:bytes=1024-

            //向服务器请求,获得服务器回应数据流
            respone = request.GetResponse();
            ns = respone.GetResponseStream();
            long totalSize = respone.ContentLength + startPos;//ContentLength  传输的数据长度 没有设置AddRange 文件全部长度 设置AddRange就是要传输的长度
            long curSize = startPos;

            LogDown(curSize, totalSize, "已下");
            if (curSize == totalSize)
            {
    
    
                if (callBack != null)
                    callBack(1, true);
            }
            else
            {
    
    
                int oneReadLen = 1024 * 8;// 一次读取长度 
                byte[] bytes = new byte[oneReadLen];
                int readSize = ns.Read(bytes, 0, oneReadLen); // 读取第一份数据
                while (readSize > 0)
                {
    
    
                    fs.Write(bytes, 0, readSize);       // 将下载到的数据写入临时文件
                    curSize += readSize;
                    readSize = ns.Read(bytes, 0, oneReadLen);// 往下继续读取
                    if (callBack != null)
                        callBack((double)curSize / totalSize, false);
                }

                if (curSize == totalSize)// 判断是否下载完成
                {
    
    
                    LogDown(curSize - startPos, totalSize, "又");
                    if (callBack != null)
                        callBack(1, true);
                }
                else
                {
    
    
                    if (errAction != null)
                    {
    
    
                        string err = "下载出错,文件大小不对,请重启后重试!";
                        Debug.LogError(err);
                        errAction(err);
                    }
                }
            }
        }
        catch (WebException ex)
        {
    
    
            if (errAction != null)
            {
    
    
                string err = "下载出错:" + ex.Message + ",请重启后重试!";
                Debug.LogError(err);
                errAction(err);
            }
        }
        finally
        {
    
    
            if (fs != null)
            {
    
    
                fs.Flush();
                fs.Close();
                fs = null;
            }
            if (ns != null)
                ns.Close();
            if (respone != null)
                respone.Close();
            if (request != null)
                request.Abort();
        }
    }

    private static void LogDown(double fileLen, long totalLength, string headStr)
    {
    
    
        double precess = fileLen / totalLength;//进度
        precess = GetPreciseDecimal(precess, 4);//精确小数点后4位
        precess *= 100;//显示百分比
        StringBuilder sb = new StringBuilder();
        sb.Append(headStr);
        sb.Append("载文件长度:");
        sb.Append(precess);
        sb.Append("%  nowFileLength:");
        sb.Append(fileLen);
        sb.Append("  totalLength:");
        sb.Append(totalLength);
        Debug.Log(sb.ToString());
    }

    /// <summary>
    /// 数字精确到后几位
    /// </summary>
    public static double GetPreciseDecimal(double nNum, int n)
    {
    
    
        float nDecimal = Mathf.Pow(10, n);
        int i = (int)(nNum * nDecimal);
        nNum = i / nDecimal;
        return nNum;
    }

    /// <summary>
    /// https webrequest因“身份验证或解密失败” 在提出请求之前 调用
    /// </summary>
    private static bool MyRemoteCertificateValidationCallback(System.Object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
    
    
        bool isOk = true;
        // If there are errors in the certificate chain,
        // look at each error to determine the cause.
        if (sslPolicyErrors != SslPolicyErrors.None)
        {
    
    
            for (int i = 0; i < chain.ChainStatus.Length; i++)
            {
    
    
                if (chain.ChainStatus[i].Status == X509ChainStatusFlags.RevocationStatusUnknown)
                    continue;
                chain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
                chain.ChainPolicy.RevocationMode = X509RevocationMode.Online;
                chain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 1, 0);
                chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
                bool chainIsValid = chain.Build((X509Certificate2)certificate);
                if (!chainIsValid)
                {
    
    
                    isOk = false;
                    break;
                }
            }
        }
        return isOk;
    }
}

二.单开线程 控制下载 显示下载进度

using System.Threading;
using UnityEngine;
using UnityEngine.UI;

public class HttpWebRequestStart : MonoBehaviour
{
    
    
    private string downUrl = "1.apk";//下载地址【需要自己替换】
    private string writePath = "";
    public Slider slider;
    public Text text;
    public Button startDown;
    private float downProcess=0;//下载进度
    private bool isDownOver = false;//是否下载结束
    private Thread thread = null;
    private double lastProcess;
    void Start()
    {
    
    
        writePath = Application.streamingAssetsPath + "/Http-Download/1.apk";
        startDown.onClick.AddListener(StartDownClick);
    }

    private void StartDownClick()
    {
    
    
        thread = new Thread(() =>
        {
    
    
            BreakpointDownloader.download(downUrl, writePath, DownErrAcTion, UpdateDownProcessData);
        });
        thread.Start();
        thread.IsBackground = true;
    }

    private void DownErrAcTion(string err)
    {
    
    
        Debug.LogError("下载错误:" + err);
    }

    private void UpdateDownProcessData(double procee,bool isOver)
    {
    
    
        downProcess = (float)procee;
        isDownOver = isOver;
        if (isOver)
            Debug.LogWarning("http下载结束");
    }

    void Update()
    {
    
    
        UpdateProcess();
    }

    private void UpdateProcess()
    {
    
    
        if (slider == null)
            return;
        if (isDownOver && slider.value == 1)
            return;
        if(slider.value== downProcess)
            return;
        slider.value = downProcess;
        double newProcess = BreakpointDownloader.GetPreciseDecimal(downProcess,3) * 100;
        if (lastProcess != newProcess)
        {
    
    
            text.text = string.Format("下载进度:{0}{1}", lastProcess, "%");
            lastProcess = newProcess;
        }
    }

    private void OnDestroy()
    {
    
    
        if (thread != null)//不停止线程 会卡死unity
            thread.Abort();
    }
}

三.下载Demo

猜你喜欢

转载自blog.csdn.net/baidu_39447417/article/details/110359445