1. 程式人生 > 其它 >.net 多執行緒下載檔案(已經測試)

.net 多執行緒下載檔案(已經測試)

public class MultiDownload
    {
        #region 初始化引數

        private int bufferSize = 512;//緩衝池大小
        private int threadNum;//執行緒數

        private string Url;//接受檔案的URL
        //private List<string> Urls;

        private bool HasMerge;//檔案合併標誌
        private string DestFileName;
        private
long receiveSize;//已經接收到的位元組數 private long fileSizeAll;//檔案位元組數 private int RowIndex;//任務索引 private List<ThreadInfo> threadinfos; private object lockPercent;//用於在載入下載進度是上鎖 private object lockFile; #endregion 初始化引數 /// <summary> /// 下載進度
/// </summary> /// <param name="rowIndex">任務索引</param> /// <param name="percent">進度</param> public delegate void DownloadingPercent(double percent); public event DownloadingPercent OnDownloadingPercent; /// <summary> /// 原始檔大小
/// </summary> /// <param name="rowIndex"></param> /// <param name="size"></param> public delegate void UpdateSrcFileSize(int rowIndex, long size); public event UpdateSrcFileSize OnUpdateSrcFileSize; /// <summary> /// 響應開始時間和結束時間的事件 /// </summary> /// <param name="date"></param> public delegate void GetRunTime(DateTime date); public event GetRunTime OnGetStartTime; public event GetRunTime OnGetFinalTime; /// <summary> /// 響應改變控制元件狀態事件,變為可使用 /// </summary> public delegate void SetControlStaus(); public event SetControlStaus OnSetControlStaus; public MultiDownload() { threadinfos = new List<ThreadInfo>(); lockPercent = new object(); lockFile = new object(); } /// <summary> /// Http方式多執行緒下載一個檔案 /// </summary> /// <param name="srcFileUrl">檔案地址</param> /// <param name="destFileName">儲存全名</param> /// <param name="maxThreadNum">執行緒數</param> /// <param name="rowIndex">任務索引</param> public void DownloadFile(string srcFileUrl, string destFileName, int maxThreadNum = 5, int rowIndex = 0) { if (OnGetStartTime != null) OnGetStartTime(DateTime.Now); Url = srcFileUrl; DestFileName = destFileName; RowIndex = rowIndex; threadNum = maxThreadNum;//多少個執行緒下載 receiveSize = 0; HttpWebRequest request = (HttpWebRequest)WebRequest.Create(this.Url); fileSizeAll = request.GetResponse().ContentLength; request.Abort(); if (OnUpdateSrcFileSize != null) OnUpdateSrcFileSize(0, fileSizeAll); //初始化多執行緒 List<Task> taskList = new List<Task>(); //每個執行緒平均分配檔案大小 long pieceSize = (long)fileSizeAll / threadNum + (long)fileSizeAll % threadNum; for (int i = 0; i < threadNum; i++) { var resetEvent = new ManualResetEvent(false); ThreadInfo currentThread = new ThreadInfo(); currentThread.ThreadId = i; currentThread.ThreadStatus = false; string filename = System.IO.Path.GetFileName(DestFileName);//帶拓展名的檔名 string dir = System.IO.Path.GetDirectoryName(DestFileName); //返回檔案所在目錄 currentThread.TmpFileName = string.Format($"{dir}{i}_{filename}.tmp"); currentThread.Url = Url; currentThread.FileName = DestFileName; long startPosition = (i * pieceSize); currentThread.StartPosition = startPosition == 0 ? 0 : startPosition + 1; currentThread.FileSize = startPosition + pieceSize; threadinfos.Add(currentThread); taskList.Add(Task.Factory.StartNew(() => { ReceiveHttp(currentThread); })); } TaskFactory taskFactory = new TaskFactory(); taskList.Add(taskFactory.ContinueWhenAll(taskList.ToArray(), tArray => { //啟動合併執行緒 MergeFile(); threadinfos.Clear(); taskList.Clear(); })); } /// <summary> /// Http方式接收一個區塊 /// </summary> private void ReceiveHttp(object thread) { FileStream fs = null; Stream ns = null; try { ThreadInfo currentThread = (ThreadInfo)thread; byte[] buffer = new byte[bufferSize]; // 接收緩衝區 if (!File.Exists(currentThread.FileName)) { fs = new FileStream(currentThread.TmpFileName, FileMode.Create); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(currentThread.Url); request.Headers["Accept-Ranges"] = "bytes"; request.AddRange(currentThread.StartPosition, currentThread.FileSize); ns = request.GetResponse().GetResponseStream(); int readSize = ns.Read(buffer, 0, bufferSize); while (readSize > 0) { fs.Write(buffer, 0, readSize); buffer = new byte[bufferSize]; readSize = ns.Read(buffer, 0, buffer.Length); receiveSize += readSize; double percent = (double)receiveSize / (double)fileSizeAll * 100; //Debug.WriteLine($"下載進度:{percent}"); if (OnDownloadingPercent != null) OnDownloadingPercent(percent);//觸發下載進度事件 } } currentThread.ThreadStatus = true; } catch //這裡不用處理異常,如果等待超時了,會自動繼續等待到可以下載為止 { //throw ex; } finally { fs?.Close(); ns?.Close(); } } /// <summary> /// 合併檔案 /// </summary> private void MergeFile() { int readSize; string downFileNamePath = DestFileName; byte[] buffer = new byte[bufferSize]; int length = 0; using (FileStream fs = new FileStream(downFileNamePath, FileMode.Create)) { foreach (var item in threadinfos.OrderBy(o => o.ThreadId)) { if (!File.Exists(item.TmpFileName)) continue; var tempFile = item.TmpFileName; Debug.WriteLine($"當前合併檔案:{tempFile}"); using (FileStream tempStream = new FileStream(tempFile, FileMode.Open)) { while ((length = tempStream.Read(buffer, 0, buffer.Length)) > 0) { fs.Write(buffer, 0, length); } tempStream.Flush(); } try { File.Delete(item.TmpFileName); } catch { } } } if (OnGetFinalTime != null) OnGetFinalTime(DateTime.Now); if (OnSetControlStaus != null) OnSetControlStaus(); } /// <summary> /// 釋放資源 /// </summary> public void Dispose() { foreach (var item in threadinfos) { File.Delete(item.TmpFileName); } } } internal class ThreadInfo { public int ThreadId { get; set; } public bool ThreadStatus { get; set; } public long StartPosition { get; set; } public long FileSize { get; set; } public string Url { get; set; } public string TmpFileName { get; set; } public string FileName { get; set; } public int Times { get; set; } }