XAsset 异步下载资源 解析

Posted 那个妹子留步

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了XAsset 异步下载资源 解析相关的知识,希望对你有一定的参考价值。

工具

资源服务器 hfs网络文件服务器(百度即可下载 hfs网络文件服务器(个人HTTP文件服务器)下载 v2.3 免费绿色版 - 比克尔下载 上传资源)

 Unity 

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

//namespace VEngine
//
    public enum DownloadStatus
    
        Wait,
        Progressing,
        Success,
        Failed
    

    public class DownloadInfo
    
        public uint crc;
        public string savePath;
        public long size;
        public string url;
    


    public class DownLoadManager
    


        /// <summary>
        /// 最大同时下载数
        /// </summary>
        public static uint MaxDownLoads = 10;

        /// <summary>
        /// 读取速度
        /// </summary>
        public static uint ReadBufferSize = 1024 * 4;

        /// <summary>
        /// 准备  下载
        /// </summary>
        public static readonly List<Download> Prepared = new List<Download>();

        /// <summary>
        /// 正在  下载
        /// </summary>
        public static readonly List<Download> Progressing = new List<Download>();

        /// <summary>
        /// 缓存下载
        /// </summary>
        public static readonly Dictionary<string, Download> Cache = new Dictionary<string, Download>();


        /// <summary>
        /// 是否有正在下载的任务
        /// </summary>
        public static bool Working => Progressing.Count > 0;


        //每秒下载速度
        public static long TotalBandwidth  get; private set; 

        /// <summary>
        /// 总下载大小
        /// </summary>
        public static long TotalDownloadedBytes
        
            get
            
                var value = 0L;
                foreach (var item in Cache)    value += item.Value.downloadedBytes; 
                return value;
            
        

        /// <summary>
        /// 总大小
        /// </summary>
        public static long TotalSize
        
            get
            
                var value = 0L;
                foreach (var item in Cache)   value += item.Value.info.size; 
                return value;
            
        

        private static float lastSampleTime;//最后下载时间
        private static long lastTotalDownLoadedBytes;//最后下载字节数

        public static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain,
SslPolicyErrors spe)
        
            return true;
        

        public static void ClearAllDownloads()
        
            foreach (var download in Progressing)
            

            

            Prepared.Clear();
            Progressing.Clear();
            Cache.Clear();
        


        public static Download DownloadAsync(string url,string savePath,long size = 0,uint crc = 0,Action<Download> complete=null)
        
            return DownloadAsync(new DownloadInfo
            
                url = url,
                savePath = savePath,
                crc = crc,
                size = size
            , complete);
        

        public static Download DownloadAsync(DownloadInfo info, Action<Download> complete)
        
            Download download;
            if (!Cache.TryGetValue(info.url,out download))
            
                download = new Download(info);
                Prepared.Add(download);
                Cache.Add(info.url, download);
            
            else
            
                UnityEngine.Debug.LogWarning($"Download url info.url already exist.");
            

            if (complete != null)
            
                download.completed += complete;
            
            return download;
        


        public static void UpdateAll()
        
            //把准备下载列表的  加入正在下载的列表中
            if (Prepared.Count > 0)
            
                //取剩余可  准备下载数   受最大数量限制
                for (int i = 0; i < Mathf.Min(Prepared.Count,MaxDownLoads - Progressing.Count); i++)
                
                    var download = Prepared[i];
                    Prepared.RemoveAt(i);
                    i--;
                    Progressing.Add(download);
                    download.Start();//启动下载
                
            

            //刷新正在下载列表的  下载进度  和移除已经下载完毕的 任务
            if (Progressing.Count > 0)
            
                for (int index = 0; index < Progressing.Count; index++)
                
                    var download = Progressing[index];
                    if (download.updated != null) download.updated(download);

                    if (download.isDone)
                    
                        if (download.status == DownloadStatus.Failed)
                        
                            Debug.LogError($"Unable to download download.info.url with error  download.error");
                        
                        else
                        
                            Debug.Log($"Success to download  download.info.url");
                        
                        Progressing.RemoveAt(index);
                        index--;
                        download.Complete();
                    

                


                //一秒间隔 刷新 进度
                if (Time.realtimeSinceStartup  - lastSampleTime >= 1)
                
                    lastTotalDownLoadedBytes = TotalDownloadedBytes;
                    lastSampleTime = Time.realtimeSinceStartup;
                
            
            else
            
                if (Cache.Count <= 0) return;

                Cache.Clear();
                lastTotalDownLoadedBytes = 0;
                lastSampleTime = Time.realtimeSinceStartup;
 
            
        

    

    public class Download : CustomYieldInstruction
    
        public Action<Download> completed  get; set; 
        public Action<Download> updated  get; set; 
        /// <summary>
        /// 下载状态
        /// </summary>
        public DownloadStatus status  get; private set; 

        /// <summary>
        /// 下载信息
        /// </summary>
        public DownloadInfo info  get; private set; 

        private Thread _thread;
        private FileStream writer;
        private readonly byte[] _readBuffer = new byte[DownLoadManager.ReadBufferSize];
        public bool isDone => status == DownloadStatus.Failed || status == DownloadStatus.Success;
        public long downloadedBytes  get; private set; //已经下载的字节数
        public float process => downloadedBytes * 1f / info.size;//下载进度
        public override bool keepWaiting =>!isDone;
        public string error  get; private set; 



        public Download(DownloadInfo info)
        
            this.info = info;
            status = DownloadStatus.Wait;
            downloadedBytes = 0;
        



        private WebRequest GetHttpWebRequest()
        
            var httpWebRequest = (HttpWebRequest)WebRequest.Create(info.url) ;
            httpWebRequest.ProtocolVersion = HttpVersion.Version10;
            if (downloadedBytes > 0) httpWebRequest.AddRange(downloadedBytes);
            return httpWebRequest;
        

        private WebRequest CreateWebRequest()
        
            WebRequest request;
            if (info.url.StartsWith("https",StringComparison.OrdinalIgnoreCase))
            
                ServicePointManager.ServerCertificateValidationCallback = DownLoadManager.CheckValidationResult;
            
            request = GetHttpWebRequest();

            return request;
        

        private void DownLoading()
        
            var request = CreateWebRequest();
            using (var response = request.GetResponse())
            
                if (response.ContentLength > 0)
                
                    //初始化下载文件 大小
                    if (info.size == 0) info.size = response.ContentLength + downloadedBytes;

                    using (var reader = response.GetResponseStream())
                    
                        if (downloadedBytes < info.size)
                        
                            while (status == DownloadStatus.Progressing)
                            
                                if (ReadToEnd(reader)) break;
                            
                        
                    
                
                else
                
                    status = DownloadStatus.Success;
                
            
        

        private bool ReadToEnd(Stream reader)
        
            var len = reader.Read(_readBuffer, 0, _readBuffer.Length);
            if (len > 0)
            
                writer.Write(_readBuffer, 0, len);
                downloadedBytes += len;
                return false;
            
            return true;
        


        private void CloseWrite()
        
            if (writer != null)
            
                writer.Flush();
                writer.Close();
            
        




        private void Run()
        
            try
            
                DownLoading();//下载 和 写入
                CloseWrite();
                if (status == DownloadStatus.Failed) return;

                //大小不匹配下载失败
                if (downloadedBytes != info.size) 
                
                    error = $"Download length downloadedBytes mismatch to info.size";
                    status = DownloadStatus.Failed;
                    return;
                


                //CRC校验
                if (info.crc != 0)
                
                    //var crc = Utility.ComputeCRC32(info.savePath);
                    //if (info.crc != crc)
                    //
                    //    File.Delete(info.savePath);
                    //    error = $"Download crc crc mismatch to info.crc";
                    //    status = DownloadStatus.Failed;
                    //    return;
                    //
                

                status = DownloadStatus.Success;
            
            catch (Exception e)
            
                CloseWrite();
                error = e.Message;
                status = DownloadStatus.Failed;
            
        

        public void Start()
        
            //等待状态才会 启动
            if (status != DownloadStatus.Wait) return;

            Debug.Log($" start download info.url");
            status = DownloadStatus.Progressing;

            var file = new FileInfo(info.savePath);
            //断点续传  还是怎么样
            if (file.Exists && file.Length > 0)
            
                if (info.size > 0 && file.Length == info.size)
                
                    status = DownloadStatus.Success;
                    return;
                

                writer = File.OpenWrite(info.savePath);
                downloadedBytes = writer.Length - 1;//只取了  -1长度
                if (downloadedBytes > 0)
                
                    writer.Seek(-1, SeekOrigin.End);
                
            
            else
            
                var dir = Path.GetDirectoryName(info.savePath);
                if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) Directory.CreateDirectory(dir);

                writer = File.Create(info.savePath);
            


            _thread = new Thread(Run)
            
                IsBackground = true
            ;

            _thread.Start();
        



        public void Retry()
        
            status = DownloadStatus.Wait;
            Start();
        

        public void Pause()
        
            status = DownloadStatus.Wait;
        
        public void UnPause()
        
            Retry();
        
        public void Cancel()
        
            error = "User Cancel.";
            status = DownloadStatus.Failed;
        
        public void Complete()
        
            if (completed != null)
            
                completed.Invoke(this);
                completed = null;
            
        
    
//
//测试下载
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

public class TestSomething : MonoBehaviour


    [ContextMenu("SceneName")]
    void Scene()
    
        TempCopy();
    
    public void TempCopy()
    
        string path = @"D:\\testDownload\\dog.gif";
        for (int i = 0; i < 100; i++)
        
            CopyDirIntoDestDirectory(path, Path.Combine(@"D:\\testDownload",$"dogi.gif"), true);
        
    

    public void CopyDirIntoDestDirectory(string sourceFileName, string destFileName, bool overwrite)
    
        File.Copy(sourceFileName, destFileName, overwrite);
    


    // Start is called before the first frame update
    void Start()
    
        TestXAssetDownload();
    
    private Download download;
    void TestXAssetDownload()
    

        for (int i = 0; i < 30; i++)
        
            var index = i;
            DownLoadManager.DownloadAsync(new DownloadInfo()
            
                savePath = Path.Combine(Application.dataPath, $"dogindex.gif"),
                url = @"http://10.0.0.155:8000/" + $"dogindex.gif"
            , (d) =>  print($"下载dogindex.gif完毕"); ); ;
        


    
    // Update is called once per frame
    void Update()
    
        DownLoadManager.UpdateAll();
    

以上是关于XAsset 异步下载资源 解析的主要内容,如果未能解决你的问题,请参考以下文章

XAsset 异步下载资源 解析

Unity关于XAsset的一个坑

组合框架序列化异步操作

区块链~Merkle Tree(默克尔树)算法解析~转载

[转帖]MerkleDAG全面解析 一文读懂什么是默克尔有向无环图

async和await异步编程资源汇总