zoukankan      html  css  js  c++  java
  • 一个简单的利用 WebClient 异步下载的示例(五)(完结篇)

    接着上一篇,我们继续来优化。我们的 SkyParallelWebClient 可否支持切换“同步下载模式”和“异步下载模式”呢,好处是大量的代码不用改,只需要调用 skyParallelWebClient.StartAsync() 就开始异步下载,而改为 skyParallelWebClient.StartSync(); 就同步下载。如图:

    同步下载:

    异步下载:

    1. 同步下载模式

    直接贴代码了:

        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private List<int> GetDownloadIds()
            {
                List<int> ids = new List<int>();
                for (int i = 1; i <= 32; i++)
                {
                    ids.Add(i);
                }
                return ids;
            }
    
            private void WhenAllDownloading()
            {
                this.listBoxLog.Items.Insert(0, string.Format("当前时间:{0},准备开始下载...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
                //禁用按钮
                EnableOrDisableButtons(false);
            }
    
            private void EnableOrDisableButtons(bool enabled)
            {
                this.btnRunByHttpClient.Enabled = enabled;
                this.btnRunByWebClient.Enabled = enabled;
            }
    
            private void WhenSingleDownloading(WhenSingleDownloadEventArgs eventArg)
            {
                this.listBoxLog.Items.Insert(0, string.Format("当前时间:{0},剩余 {1} 个。编号 {2} 准备开始下载...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), eventArg.RemainingQueueCount, eventArg.CurrentDownloadEntry.Data));
            }
    
            private void WhenSingleDownloaded(WhenSingleDownloadEventArgs eventArg)
            {
                this.listBoxLog.Items.Insert(0, string.Format("当前时间:{0},剩余 {1} 个。编号 {2} 下载完毕...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), eventArg.RemainingQueueCount, eventArg.CurrentDownloadEntry.Data));
            }
    
            private void WhenAllDownloaded()
            {
                this.listBoxLog.Items.Insert(0, string.Format("当前时间:{0},下载完毕!", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
                //启用按钮
                EnableOrDisableButtons(true);
            }
    
            private async void btnRunByHttpClient_Click(object sender, EventArgs e)
            {
                //SkyHttpClient skyHttpClient = new SkyHttpClient();
                //try
                //{
                //    WhenAllDownloading();
                //    foreach (var id in GetDownloadIds())
                //    {
                //        bool singleDownloadSuccess = await TaskDemo101.RunByHttpClient(skyHttpClient, id);
                //        WhenSingleDownloading(id);
                //    }
                //    WhenAllDownloaded();
                //}
                //catch (Exception ex)
                //{
                //    MessageBox.Show(ex.Message, "Download Error!");
                //}
            }
    
            private void btnRunByWebClient_Click(object sender, EventArgs e)
            {
                WhenAllDownloading();
                var ids = GetDownloadIds();
                List<DownloadEntry> downloadConfigs = new List<DownloadEntry>();
                Random rd = new Random();
                foreach (var id in ids)
                {
                    downloadConfigs.Add(new DownloadEntry(TaskDemo101.GetRandomUrl(rd), TaskDemo101.GetSavedFileFullName(), id));
                }
                // 方案4(同步下载,需要启动另外一个线程)
                ThreadPool.QueueUserWorkItem(new WaitCallback(RunByWebClientCore), downloadConfigs);
            }
    
            private void RunByWebClientCore(object state)
            {
                List<DownloadEntry> downloadConfigs = (List<DownloadEntry>)state;
                SkyParallelWebClient skyParallelWebClient = new SkyParallelWebClient(downloadConfigs, 10);
                skyParallelWebClient.SetMaximumForProgress += SkyParallelWebClient_SetMaximumForProgress;
                skyParallelWebClient.SetRealTimeValueForProgress += SkyParallelWebClient_SetRealTimeValueForProgress;
                skyParallelWebClient.WhenAllDownloaded += SkyWebClient_WhenAllDownloaded;
                skyParallelWebClient.WhenSingleDownloading += SkyWebClient_WhenSingleDownloading;
                skyParallelWebClient.WhenSingleDownloaded += SkyWebClient_WhenSingleDownloaded;
                skyParallelWebClient.WhenDownloadingError += SkyWebClient_WhenDownloadingError;
                skyParallelWebClient.StartSync();
            }
    
            private void SkyParallelWebClient_SetRealTimeValueForProgress(int realTimeValue)
            {
                this.progressBar1.Value = realTimeValue;
            }
    
            private void SkyParallelWebClient_SetMaximumForProgress(int maximum)
            {
                this.progressBar1.Maximum = maximum;
            }
    
            private void SkyWebClient_WhenDownloadingError(Exception ex)
            {
                MessageBox.Show("下载时出现错误: " + ex.Message);
            }
    
            private void SkyWebClient_WhenSingleDownloading(WhenSingleDownloadEventArgs eventArg)
            {
                WhenSingleDownloading(eventArg);
            }
    
            private void SkyWebClient_WhenSingleDownloaded(WhenSingleDownloadEventArgs eventArg)
            {
                WhenSingleDownloaded(eventArg);
            }
    
            private void SkyWebClient_WhenAllDownloaded()
            {
                btnRunByWebClient.Text = "用 WebClient 开始下载";
                WhenAllDownloaded();
            }
    
            
        }

    运行截图:

    2. 异步下载模式

    直接贴代码了:

        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            private List<int> GetDownloadIds()
            {
                List<int> ids = new List<int>();
                for (int i = 1; i <= 32; i++)
                {
                    ids.Add(i);
                }
                return ids;
            }
    
            private void WhenAllDownloading()
            {
                this.listBoxLog.Items.Insert(0, string.Format("当前时间:{0},准备开始下载...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
                //禁用按钮
                EnableOrDisableButtons(false);
            }
    
            private void EnableOrDisableButtons(bool enabled)
            {
                this.btnRunByHttpClient.Enabled = enabled;
                this.btnRunByWebClient.Enabled = enabled;
            }
    
            private void WhenSingleDownloading(WhenSingleDownloadEventArgs eventArg)
            {
                this.listBoxLog.Items.Insert(0, string.Format("当前时间:{0},剩余 {1} 个。编号 {2} 准备开始下载...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), eventArg.RemainingQueueCount, eventArg.CurrentDownloadEntry.Data));
            }
    
            private void WhenSingleDownloaded(WhenSingleDownloadEventArgs eventArg)
            {
                this.listBoxLog.Items.Insert(0, string.Format("当前时间:{0},剩余 {1} 个。编号 {2} 下载完毕...", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), eventArg.RemainingQueueCount, eventArg.CurrentDownloadEntry.Data));
            }
    
            private void WhenAllDownloaded()
            {
                this.listBoxLog.Items.Insert(0, string.Format("当前时间:{0},下载完毕!", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")));
                //启用按钮
                EnableOrDisableButtons(true);
            }
    
            private async void btnRunByHttpClient_Click(object sender, EventArgs e)
            {
                //SkyHttpClient skyHttpClient = new SkyHttpClient();
                //try
                //{
                //    WhenAllDownloading();
                //    foreach (var id in GetDownloadIds())
                //    {
                //        bool singleDownloadSuccess = await TaskDemo101.RunByHttpClient(skyHttpClient, id);
                //        WhenSingleDownloading(id);
                //    }
                //    WhenAllDownloaded();
                //}
                //catch (Exception ex)
                //{
                //    MessageBox.Show(ex.Message, "Download Error!");
                //}
            }
    
            private void btnRunByWebClient_Click(object sender, EventArgs e)
            {
                WhenAllDownloading();
                var ids = GetDownloadIds();
                List<DownloadEntry> downloadConfigs = new List<DownloadEntry>();
                Random rd = new Random();
                foreach (var id in ids)
                {
                    downloadConfigs.Add(new DownloadEntry(TaskDemo101.GetRandomUrl(rd), TaskDemo101.GetSavedFileFullName(), id));
                }
                //搜索: Parallel WebClient
                // 方案1
                //SkyWebClient skyWebClient = new SkyWebClient(downloadConfigs, this.progressBar1);
                //skyWebClient.WhenAllDownloaded += SkyWebClient_WhenAllDownloaded;
                //skyWebClient.WhenSingleDownloading += SkyWebClient_WhenSingleDownloading;
                //skyWebClient.WhenDownloadingError += SkyWebClient_WhenDownloadingError;
                //skyWebClient.Start();
                // 方案2(代码已经调整,无法恢复)
                //ThreadPool.QueueUserWorkItem(new WaitCallback(AsyncRunSkyParallelWebClient), downloadConfigs);
                // 方案3(异步下载,完美)
                SkyParallelWebClient skyParallelWebClient = new SkyParallelWebClient(downloadConfigs, 10);
                skyParallelWebClient.SetMaximumForProgress += SkyParallelWebClient_SetMaximumForProgress;
                skyParallelWebClient.SetRealTimeValueForProgress += SkyParallelWebClient_SetRealTimeValueForProgress;
                skyParallelWebClient.WhenAllDownloaded += SkyWebClient_WhenAllDownloaded;
                skyParallelWebClient.WhenSingleDownloading += SkyWebClient_WhenSingleDownloading;
                skyParallelWebClient.WhenSingleDownloaded += SkyWebClient_WhenSingleDownloaded;
                skyParallelWebClient.WhenDownloadingError += SkyWebClient_WhenDownloadingError;
                skyParallelWebClient.StartSync();
                // 方案4(同步下载,需要启动另外一个线程)
                //ThreadPool.QueueUserWorkItem(new WaitCallback(RunByWebClientCore), downloadConfigs);
            }
    
            private void RunByWebClientCore(object state)
            {
                List<DownloadEntry> downloadConfigs = (List<DownloadEntry>)state;
                SkyParallelWebClient skyParallelWebClient = new SkyParallelWebClient(downloadConfigs, 10);
                skyParallelWebClient.SetMaximumForProgress += SkyParallelWebClient_SetMaximumForProgress;
                skyParallelWebClient.SetRealTimeValueForProgress += SkyParallelWebClient_SetRealTimeValueForProgress;
                skyParallelWebClient.WhenAllDownloaded += SkyWebClient_WhenAllDownloaded;
                skyParallelWebClient.WhenSingleDownloading += SkyWebClient_WhenSingleDownloading;
                skyParallelWebClient.WhenSingleDownloaded += SkyWebClient_WhenSingleDownloaded;
                skyParallelWebClient.WhenDownloadingError += SkyWebClient_WhenDownloadingError;
                skyParallelWebClient.StartSync();
            }
    
            private void SkyParallelWebClient_SetRealTimeValueForProgress(int realTimeValue)
            {
                this.progressBar1.Value = realTimeValue;
            }
    
            private void SkyParallelWebClient_SetMaximumForProgress(int maximum)
            {
                this.progressBar1.Maximum = maximum;
            }
    
            private void SkyWebClient_WhenDownloadingError(Exception ex)
            {
                MessageBox.Show("下载时出现错误: " + ex.Message);
            }
    
            private void SkyWebClient_WhenSingleDownloading(WhenSingleDownloadEventArgs eventArg)
            {
                WhenSingleDownloading(eventArg);
            }
    
            private void SkyWebClient_WhenSingleDownloaded(WhenSingleDownloadEventArgs eventArg)
            {
                WhenSingleDownloaded(eventArg);
            }
    
            private void SkyWebClient_WhenAllDownloaded()
            {
                btnRunByWebClient.Text = "用 WebClient 开始下载";
                WhenAllDownloaded();
            }
            
        }

    运行截图:

    如上图:当打印了“下载完毕”后,仍然打印了许多日志,这是因为异步下载的回调是不定时的。为了避免这种情况,建议 WhenSingleDownloading 事件和 WhenSingleDownloaded 事件二选一,因为实在没有必要下载开始前和下载后都打印日志。我们再次修改代码后,得到如下图的日志。

    日志是不是清晰了很多?一般下载完成,可以注册完成事件,事件里面不用打印日志,而做一些比如“修改数据库表记录的状态字段”等等。

    3. 总结

    SkyParallelWebClient 完整的代码如下:

        /// <summary>
        /// 设置进度条的最大值的事件处理
        /// </summary>
        /// <param name="maximum"></param>
        public delegate void SetMaximumForProgressEventHandler(int maximum);
    
        /// <summary>
        /// 设置进度条的当前实时的值的事件处理
        /// </summary>
        /// <param name="maximum"></param>
        public delegate void SetRealTimeValueForProgressEventHandler(int realTimeValue);
    
        /// <summary>
        /// 并行的 WebClient
        /// </summary>
        public class SkyParallelWebClient : SkyWebClientBase
        {
            ConcurrentQueue<DownloadEntry> OptionDataList = new ConcurrentQueue<DownloadEntry>(); //比如说:有 500 个元素
    
            ConcurrentDictionary<WebClient, DownloadEntry> ProcessingTasks = new ConcurrentDictionary<WebClient, DownloadEntry>(); //当前运行中的
    
            public event SetMaximumForProgressEventHandler SetMaximumForProgress;
            protected virtual void OnSetMaximumForProgress(int maximum)
            {
                if (SetMaximumForProgress != null)
                {
                    SetMaximumForProgress(maximum);
                }
            }
    
            public event SetRealTimeValueForProgressEventHandler SetRealTimeValueForProgress;
            protected virtual void OnSetRealTimeValueForProgress(int realTimeValue)
            {
                if (SetRealTimeValueForProgress != null)
                {
                    SetRealTimeValueForProgress(realTimeValue);
                }
            }
    
            public int ParallelCount { get; }
    
            public bool IsCompleted { get; private set; }
    
            public int Maximum { get;}
    
            private static object lockObj = new object();
    
            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="downloadConfigs">要下载的全部集合,比如 N 多要下载的,N无限制</param>
            public SkyParallelWebClient(IEnumerable<DownloadEntry> downloadConfigs)
                :this(downloadConfigs, 20)
            {
    
            }
    
            /// <summary>
            /// 构造函数
            /// </summary>
            /// <param name="downloadConfigs">要下载的全部集合,比如 N 多要下载的,N无限制</param>
            /// <param name="parallelCount">单位内,并行下载的个数。切忌:该数字不能过大,否则可能很多文件因为 WebClient 超时,而导致乱文件(即:文件不完整)一般推荐 20 个左右</param>
            public SkyParallelWebClient(IEnumerable<DownloadEntry> downloadConfigs, int parallelCount)
            {
                if (downloadConfigs == null)
                {
                    throw new ArgumentNullException("downloadConfigs");
                }
                this.ParallelCount = parallelCount;
                foreach (var item in downloadConfigs)
                {
                    OptionDataList.Enqueue(item);
                }
                this.Maximum = OptionDataList.Count;
                System.Net.ServicePointManager.DefaultConnectionLimit = int.MaxValue;
                OnSetMaximumForProgress(this.Maximum);
            }
    
            /// <summary>
            /// 异步启动(备注:由于内部采用异步下载,所以方法不用加 Try 和返回值)
            /// </summary>
            public void StartAsync()
            {
                StartAsyncCore();
            }
    
            protected void StartAsyncCore()
            {
                if (OptionDataList.Count <= 0)
                {
                    SetIsCompletedTrue();
                    return;
                }
                while (OptionDataList.Count > 0 && ProcessingTasks.Count <= ParallelCount)
                {
                    DownloadEntry downloadEntry;
                    if (!OptionDataList.TryDequeue(out downloadEntry))
                    {
                        break;
                    }
                    DownloadFileAsync(downloadEntry);
                    OnWhenSingleDownloading(new WhenSingleDownloadEventArgs
                    {
                        CurrentDownloadEntry = downloadEntry,
                        RemainingQueueCount = OptionDataList.Count
                    });
                }
            }
    
            /// <summary>
            /// 同步启动
            /// </summary>
            public void StartSync()
            {
                StartSyncCore();
            }
    
            /// <summary>
            /// 同步启动
            /// </summary>
            public void StartSync(WebClient webClient)
            {
                StartSyncCore(webClient);
            }
    
            protected void StartSyncCore()
            {
                using (WebClient webClient = new WebClient())
                {
                    StartSyncCore(webClient);
                }
            }
    
            protected void StartSyncCore(WebClient webClient)
            {
                while (OptionDataList.Count > 0)
                {
                    DownloadEntry downloadEntry;
                    if (!OptionDataList.TryDequeue(out downloadEntry))
                    {
                        break;
                    }
                    OnWhenSingleDownloading(new WhenSingleDownloadEventArgs
                    {
                        CurrentDownloadEntry = downloadEntry,
                        RemainingQueueCount = OptionDataList.Count
                    });
                    DownloadFile(downloadEntry, webClient);
                    OnWhenSingleDownloaded(new WhenSingleDownloadEventArgs
                    {
                        CurrentDownloadEntry = downloadEntry,
                        RemainingQueueCount = OptionDataList.Count
                    });
                }
                SetIsCompletedTrue();
            }
    
            protected void SetIsCompletedTrue()
            {
                if (!IsCompleted)
                {
                    lock (lockObj)
                    {
                        if (!IsCompleted)
                        {
                            OnSetRealTimeValueForProgress(100);//表示已经完成
                            OnWhenAllDownloaded();
                            IsCompleted = true;
                        }
                    }
                }
            }
    
            private void DownloadFileAsync(DownloadEntry downloadEntry)
            {
                using (WebClient webClient = new WebClient())
                {
                    webClient.Proxy = null;
                    webClient.DownloadFileCompleted += WebClient_DownloadFileCompleted;
                    webClient.DownloadFileAsync(new Uri(downloadEntry.Url), downloadEntry.Path);
                    ProcessingTasks.TryAdd(webClient, downloadEntry);
                }
            }
    
            private void DownloadFile(DownloadEntry downloadEntry)
            {
                using (WebClient webClient = new WebClient())
                {
                    DownloadFile(downloadEntry, webClient);
                }
            }
    
            private void DownloadFile(DownloadEntry downloadEntry, WebClient webClient)
            {
                try
                {
                    webClient.Proxy = null;
                    webClient.DownloadFile(new Uri(downloadEntry.Url), downloadEntry.Path);
                }
                catch (Exception ex)
                {
                    OnWhenDownloadingError(ex);
                }
            }
    
            private void WebClient_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
            {
                WebClient webClient = (WebClient)sender;
                DownloadEntry downloadEntry;
                if (ProcessingTasks.TryRemove(webClient, out downloadEntry))
                {
                    OnWhenSingleDownloaded(new WhenSingleDownloadEventArgs
                    {
                        CurrentDownloadEntry = downloadEntry,
                        RemainingQueueCount = OptionDataList.Count
                    });
                    try
                    {
                        int realTimeValue = (this.Maximum - OptionDataList.Count) * 100 / this.Maximum;
                        OnSetRealTimeValueForProgress(realTimeValue);   //表示单个已经完成
                    }
                    catch (Exception)
                    {
    
                    }
                }
                StartAsyncCore();
            }
        }

    谢谢浏览!

  • 相关阅读:
    Floyd算法
    递归函数的学习
    动态联编【转】
    哈希hash
    sizeof与strlen
    写入文件和读取文件信息—Java Card开发第三篇
    文件系统创建—Java Card开发第二篇
    获取缓冲区内容与将缓冲区内容返回—Java card开发第一篇
    i++与++i
    电脑无法登陆ftp
  • 原文地址:https://www.cnblogs.com/Music/p/WebClient-DownloadFileAsync5.html
Copyright © 2011-2022 走看看