• .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; }
        }
  • 相关阅读:
    for循环中创建线程执行问题
    MySQL学习总结之路(第六章:表类型【存储引擎】的选择)
    Tensorflow的下载和安装
    C# 和 Python 的 hash_md5加密方法
    MySQL学习总结之路(第五章:函数)
    MySQL学习总结之路(第四章:运算符)
    MySQL学习总结之路(第三章:数据类型)
    MySQL学习总结之路(第二章:表)
    MySQL学习总结之路(服务与数据库管理)
    CSS居中的方式15种(水平垂直)
  • 原文地址:https://www.cnblogs.com/Mr-lin66/p/14940301.html
Copyright © 2020-2023  润新知