using System.Net; using System.IO; namespace Zerolauncher.Manager { public class DownloadManager { /// /// 主线程 /// private SynchronizationContext _mainThreadSynContext; /// /// 下载网址 /// public string Url; public event Action OnError; private static object errorlock = new object(); /// /// 主要用于关闭线程 /// private bool _isDownload = false; public DownloadManager(string url) { // 主线程赋值 _mainThreadSynContext = SynchronizationContext.Current; // 突破Http协议的并发连接数限制 ServicePointManager.DefaultConnectionLimit = 512; Url = url; } /// /// 查询文件大小 /// /// public long GetFileSize() { HttpWebRequest request; HttpWebResponse response; try { request = (HttpWebRequest)WebRequest.CreateHttp(new Uri(Url)); request.Method = "HEAD"; response = (HttpWebResponse)request.GetResponse(); // 获得文件长度 long contentLength = response.ContentLength; response.Close(); request.Abort(); return contentLength; } catch (Exception ex) { onError(ex); // throw; return -1; } } /// /// 异步查询文件大小 /// /// public void GetFileSizeAsyn(Action onTrigger = null) { ThreadStart threadStart = new ThreadStart(() => { PostMainThreadAction(onTrigger, GetFileSize()); }); Thread thread = new Thread(threadStart); thread.Start(); } /// /// 多线程下载文件至本地 /// /// 线程总数 /// 保存文件路径 /// 下载过程回调(已下载文件大小、总文件大小) /// 下载完毕回调(下载文件数据) public void DownloadToFile(int threadCount, string filePath, Action onDownloading = null, Action onTrigger = null) { _isDownload = true; long csize = 0; //已下载大小 int ocnt = 0; //完成线程数 // 下载逻辑 GetFileSizeAsyn((size) => { if (size == -1) return; // 准备工作 var tempFilePaths = new string[threadCount]; var tempFileFileStreams = new FileStream[threadCount]; var dirPath = Path.GetDirectoryName(filePath); var fileName = Path.GetFileName(filePath); // 下载根目录不存在则创建 if (!Directory.Exists(dirPath)) { Directory.CreateDirectory(dirPath); } // 查看下载临时文件是否可以继续断点续传 var fileInfos = new DirectoryInfo(dirPath).GetFiles(fileName + "*.temp"); if (fileInfos.Length != threadCount) { // 下载临时文件数量不相同,则清理 foreach (var info in fileInfos) { info.Delete(); } } // 创建下载临时文件,并创建文件流 for (int i = 0; i < threadCount; i++) { tempFilePaths[i] = filePath + i + ".temp"; if (!File.Exists(tempFilePaths[i])) { File.Create(tempFilePaths[i]).Dispose(); } tempFileFileStreams[i] = File.OpenWrite(tempFilePaths[i]); tempFileFileStreams[i].Seek(tempFileFileStreams[i].Length, System.IO.SeekOrigin.Current); csize += tempFileFileStreams[i].Length; } // 单线程下载过程回调函数 Action t_onDownloading = (index, rsize, rbytes, data) => { csize += rsize; tempFileFileStreams[index].Write(rbytes, 0, (int)rsize); PostMainThreadAction(onDownloading, csize, size); }; // 单线程下载完毕回调函数 Action t_onTrigger = (index, data) => { // 关闭文件流 tempFileFileStreams[index].Close(); ocnt++; if (ocnt >= threadCount) { // 将临时文件转为下载文件 if (!File.Exists(filePath)) { File.Create(filePath).Dispose(); } else { File.WriteAllBytes(filePath, new byte[] { }); } FileStream fs = File.OpenWrite(filePath); fs.Seek(fs.Length, System.IO.SeekOrigin.Current); foreach (var tempPath in tempFilePaths) { var tempData = File.ReadAllBytes(tempPath); fs.Write(tempData, 0, tempData.Length); File.Delete(tempPath); } fs.Close(); PostMainThreadAction(onTrigger, File.ReadAllBytes(filePath)); } }; // 分割文件尺寸,多线程下载 long[] sizes = SplitFileSize(size, threadCount); for (int i = 0; i < sizes.Length; i = i + 2) { long from = sizes[i]; long to = sizes[i + 1]; // 断点续传 from += tempFileFileStreams[i / 2].Length; if (from >= to) { t_onTrigger(i / 2, null); continue; } _threadDownload(i / 2, from, to, t_onDownloading, t_onTrigger); } }); } /// /// 多线程下载文件至内存 /// /// 线程总数 /// 下载过程回调(已下载文件大小、总文件大小) /// 下载完毕回调(下载文件数据) public void DownloadToMemory(int threadCount, Action onDownloading = null, Action onTrigger = null) { _isDownload = true; long csize = 0; // 已下载大小 int ocnt = 0; // 完成线程数 byte[] cdata; // 已下载数据 // 下载逻辑 GetFileSizeAsyn((size) => { cdata = new byte[size]; // 单线程下载过程回调函数 Action t_onDownloading = (index, rsize, rbytes, data) => { csize += rsize; PostMainThreadAction(onDownloading, csize, size); }; // 单线程下载完毕回调函数 Action t_onTrigger = (index, data) => { long dIndex = (long)Math.Ceiling((double)(size * index / threadCount)); Array.Copy(data, 0, cdata, dIndex, data.Length); ocnt++; if (ocnt >= threadCount) { PostMainThreadAction(onTrigger, cdata); } }; // 分割文件尺寸,多线程下载 long[] sizes = SplitFileSize(size, threadCount); for (int i = 0; i < sizes.Length; i = i + 2) { long from = sizes[i]; long to = sizes[i + 1]; _threadDownload(i / 2, from, to, t_onDownloading, t_onTrigger); } }); } /// /// 单线程下载 /// /// 线程ID /// 下载起始位置 /// 下载结束位置 /// 下载过程回调(线程ID、单次下载数据大小、单次下载数据缓存区、已下载文件数据) /// 下载完毕回调(线程ID、下载文件数据) private void _threadDownload(int index, long from, long to, Action onDownloading = null, Action onTrigger = null) { Thread thread = new Thread(new ThreadStart(() => { try { var request = (HttpWebRequest)WebRequest.Create(new Uri(Url)); request.AddRange(from, to); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream ns = response.GetResponseStream(); byte[] rbytes = new byte[8 * 1024]; int rSize = 0; MemoryStream ms = new MemoryStream(); while (true) { if (!_isDownload) return; rSize = ns.Read(rbytes, 0, rbytes.Length); if (rSize <= 0) break; ms.Write(rbytes, 0, rSize); if (onDownloading != null) onDownloading(index, rSize, rbytes, ms.ToArray()); } ns.Close(); response.Close(); request.Abort(); if (ms.Length == (to - from) + 1) { if (onTrigger != null) onTrigger(index, ms.ToArray()); } else { lock (errorlock) { if (_isDownload) { onError(new Exception("文件大小校验不通过")); } } } } catch (Exception ex) { onError(ex); } })); thread.Start(); } public void Close() { _isDownload = false; } /// /// 分割文件大小 /// /// private long[] SplitFileSize(long size, int count) { long[] result = new long[count * 2]; for (int i = 0; i < count; i++) { long from = (long)Math.Ceiling((double)(size * i / count)); long to = (long)Math.Ceiling((double)(size * (i + 1) / count)) - 1; result[i * 2] = from; result[i * 2 + 1] = to; } return result; } private void onError(Exception ex) { Close(); PostMainThreadAction(OnError, ex); } /// /// 通知主线程回调 /// private void PostMainThreadAction(Action action) { _mainThreadSynContext.Post(new SendOrPostCallback((o) => { Action e = (Action)o.GetType().GetProperty("action").GetValue(o); if (e != null) e(); }), new { action = action }); } private void PostMainThreadAction(Action action, T arg1) { _mainThreadSynContext.Post(new SendOrPostCallback((o) => { Action e = (Action)o.GetType().GetProperty("action").GetValue(o); T t1 = (T)o.GetType().GetProperty("arg1").GetValue(o); if (e != null) e(t1); }), new { action = action, arg1 = arg1 }); } public void PostMainThreadAction(Action action, T1 arg1, T2 arg2) { _mainThreadSynContext.Post(new SendOrPostCallback((o) => { Action e = (Action)o.GetType().GetProperty("action").GetValue(o); T1 t1 = (T1)o.GetType().GetProperty("arg1").GetValue(o); T2 t2 = (T2)o.GetType().GetProperty("arg2").GetValue(o); if (e != null) e(t1, t2); }), new { action = action, arg1 = arg1, arg2 = arg2 }); } public void PostMainThreadAction(Action action, T1 arg1, T2 arg2, T3 arg3) { _mainThreadSynContext.Post(new SendOrPostCallback((o) => { Action e = (Action)o.GetType().GetProperty("action").GetValue(o); T1 t1 = (T1)o.GetType().GetProperty("arg1").GetValue(o); T2 t2 = (T2)o.GetType().GetProperty("arg2").GetValue(o); T3 t3 = (T3)o.GetType().GetProperty("arg3").GetValue(o); if (e != null) e(t1, t2, t3); }), new { action = action, arg1 = arg1, arg2 = arg2, arg3 = arg3 }); } public void PostMainThreadAction(Action action, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { _mainThreadSynContext.Post(new SendOrPostCallback((o) => { Action e = (Action)o.GetType().GetProperty("action").GetValue(o); T1 t1 = (T1)o.GetType().GetProperty("arg1").GetValue(o); T2 t2 = (T2)o.GetType().GetProperty("arg2").GetValue(o); T3 t3 = (T3)o.GetType().GetProperty("arg3").GetValue(o); T4 t4 = (T4)o.GetType().GetProperty("arg4").GetValue(o); if (e != null) e(t1, t2, t3, t4); }), new { action = action, arg1 = arg1, arg2 = arg2, arg3 = arg3, arg4 = arg4 }); } } }