| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 | 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 Download : CustomYieldInstruction    {        public static uint MaxDownloads = 10;        public static uint ReadBufferSize = 1042 * 4;        public static readonly List<Download> Prepared = new List<Download>();        public static readonly List<Download> Progressing = new List<Download>();        public static readonly Dictionary<string, Download> Cache = new Dictionary<string, Download>();        private static float lastSampleTime;        private static long lastTotalDownloadedBytes;        private readonly byte[] _readBuffer = new byte[ReadBufferSize];        private Thread _thread;        private FileStream writer;        private Download()        {            status = DownloadStatus.Wait;            downloadedBytes = 0;        }        public DownloadInfo info { get; private set; }        public DownloadStatus status { get; private set; }        public string error { get; private set; }        public Action<Download> completed { get; set; }        public Action<Download> updated { get; set; }        public bool isDone => status == DownloadStatus.Failed || status == DownloadStatus.Success;        public float progress => downloadedBytes * 1f / info.size;        public long downloadedBytes { get; private set; }        public override bool keepWaiting => !isDone;        public static bool Working => Progressing.Count > 0;        public static long TotalDownloadedBytes        {            get            {                var value = 0L;                foreach (var item in Cache) value += item.Value.downloadedBytes;                return value;            }        }        public static long TotalSize        {            get            {                var value = 0L;                foreach (var item in Cache) value += item.Value.info.size;                return value;            }        }        public static long TotalBandwidth { get; private set; }        public static void ClearAllDownloads()        {            foreach (var download in Progressing) download.Cancel();            Prepared.Clear();            Progressing.Clear();            Cache.Clear();        }        public static Download DownloadAsync(string url, string savePath, Action<Download> completed = null,            long size = 0, uint crc = 0)        {            return DownloadAsync(new DownloadInfo            {                url = url,                savePath = savePath,                crc = crc,                size = size            }, completed);        }        public static Download DownloadAsync(DownloadInfo info, Action<Download> completed = null)        {            Download download;            if (!Cache.TryGetValue(info.url, out download))            {                download = new Download                {                    info = info                };                Prepared.Add(download);                Cache.Add(info.url, download);            }            else            {                Logger.W("Download url {0} already exist.", info.url);            }            if (completed != null) download.completed += completed;            return download;        }        public static void UpdateAll()        {            if (Prepared.Count > 0)                for (var index = 0; index < Mathf.Min(Prepared.Count, MaxDownloads - Progressing.Count); index++)                {                    var download = Prepared[index];                    Prepared.RemoveAt(index);                    index--;                    Progressing.Add(download);                    download.Start();                }            if (Progressing.Count > 0)            {                for (var 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)                            Logger.E("Unable to download {0} with error {1}", download.info.url, download.error);                        else                            Logger.I("Success to download {0}", download.info.url);                        Progressing.RemoveAt(index);                        index--;                        download.Complete();                    }                }                if (Time.realtimeSinceStartup - lastSampleTime >= 1)                {                    TotalBandwidth = TotalDownloadedBytes - lastTotalDownloadedBytes;                    lastTotalDownloadedBytes = TotalDownloadedBytes;                    lastSampleTime = Time.realtimeSinceStartup;                }            }            else            {                if (Cache.Count <= 0) return;                Cache.Clear();                lastTotalDownloadedBytes = 0;                lastSampleTime = Time.realtimeSinceStartup;            }        }        public void Retry()        {            status = DownloadStatus.Wait;            Start();        }        public void UnPause()        {            Retry();        }        public void Pause()        {            status = DownloadStatus.Wait;        }        public void Cancel()        {            error = "User Cancel.";            status = DownloadStatus.Failed;        }        private void Complete()        {            if (completed != null)            {                completed.Invoke(this);                completed = null;            }        }        private void Run()        {            try            {                Downloading();                CloseWrite();                if (status == DownloadStatus.Failed) return;                if (downloadedBytes != info.size)                {                    error = $"Download lenght {downloadedBytes} mismatch to {info.size}";                    status = DownloadStatus.Failed;                    return;                }                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;            }        }        private void CloseWrite()        {            if (writer != null)            {                writer.Flush();                writer.Close();            }        }        private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain,            SslPolicyErrors spe)        {            return true;        }        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 WebRequest CreateWebRequest()        {            WebRequest request;            if (info.url.StartsWith("https", StringComparison.OrdinalIgnoreCase))            {                ServicePointManager.ServerCertificateValidationCallback = CheckValidationResult;                request = GetHttpWebRequest();            }            else            {                request = GetHttpWebRequest();            }            return request;        }        private WebRequest GetHttpWebRequest()        {            var httpWebRequest = (HttpWebRequest) WebRequest.Create(info.url);            httpWebRequest.ProtocolVersion = HttpVersion.Version10;            if (downloadedBytes > 0) httpWebRequest.AddRange(downloadedBytes);            return httpWebRequest;        }        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 Start()        {            if (status != DownloadStatus.Wait) return;            Logger.I("Start download {0}", 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;                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);                downloadedBytes = 0;            }            _thread = new Thread(Run)            {                IsBackground = true            };            _thread.Start();        }    }}
 |