一个简单的利用 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 = ; i <= ; i++)
- {
- ids.Add(i);
- }
- return ids;
- }
- private void WhenAllDownloading()
- {
- this.listBoxLog.Items.Insert(, 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(, 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(, 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(, 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, );
- 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 = ; i <= ; i++)
- {
- ids.Add(i);
- }
- return ids;
- }
- private void WhenAllDownloading()
- {
- this.listBoxLog.Items.Insert(, 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(, 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(, 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(, 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, );
- 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, );
- 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, )
- {
- }
- /// <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 <= )
- {
- SetIsCompletedTrue();
- return;
- }
- while (OptionDataList.Count > && 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 > )
- {
- 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();//表示已经完成
- 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) * / this.Maximum;
- OnSetRealTimeValueForProgress(realTimeValue); //表示单个已经完成
- }
- catch (Exception)
- {
- }
- }
- StartAsyncCore();
- }
- }
谢谢浏览!
一个简单的利用 WebClient 异步下载的示例(五)(完结篇)的更多相关文章
- 一个简单的利用 WebClient 异步下载的示例(三)
继续上一篇 一个简单的利用 WebClient 异步下载的示例(二) 后,继续优化它. 1. 直接贴代码了: DownloadEntry: public class DownloadEntry { p ...
- 一个简单的利用 WebClient 异步下载的示例(二)
继上一篇 一个简单的利用 WebClient 异步下载的示例(一) 后,我想把核心的处理提取出来,成 SkyWebClient,如下: 1. SkyWebClient 该构造函数中 downloadC ...
- 一个简单的利用 WebClient 异步下载的示例(一)
继上一篇文章 一个简单的利用 HttpClient 异步下载的示例 ,我们知道不管是 HttpClient,还算 WebClient,都不建议每次调用都 new HttpClient,或 new We ...
- 一个简单的利用 WebClient 异步下载的示例(四)
接上一篇,我们继续优化它. 1. DownloadEntry 类 public class DownloadEntry { public string Url { get; set; } public ...
- 一个简单的利用 HttpClient 异步下载的示例
可能你还会喜欢 一个简单的利用 WebClient 异步下载的示例 ,且代码更加新. 1. 定义自己的 HttpClient 类. using System; using System.Collec ...
- VC6下OpenGL 开发环境的构建外加一个简单的二维网络棋盘绘制示例
一.安装GLUT 工具包 GLUT 不是OpenGL 所必须的,但它会给我们的学习带来一定的方便,推荐安装. Windows 环境下的GLUT 本地下载地址:glut-install.zip(大小约为 ...
- 一个简单的AXIS远程调用Web Service示例
我们通常都将编写好的Web Service发布在Tomcat或者其他应用服务器上,然后通过浏览器调用该Web Service,返回规范的XML文件.但是如果我们不通过浏览器调用,而是通过客户端程序调用 ...
- Java RMI 实现一个简单的GFS(谷歌文件系统)——背景与设计篇
目录 背景 系统设计 1. 系统功能 2. Master组件 2.1 命名空间 2.2 心跳机制 2.3 故障恢复和容错机制 3. ChunkServer组件 3.1 本地存储 3.2 内存命中机制 ...
- 使用ssm(spring+springMVC+mybatis)创建一个简单的查询实例(三)(错误整理篇)
使用ssm(spring+springMVC+mybatis)创建一个简单的查询实例(一) 使用ssm(spring+springMVC+mybatis)创建一个简单的查询实例(二) 以上两篇已经把流 ...
随机推荐
- laravel中视图的基本使用(七)
laravel中的视图默认保存在 resources\views 目录下.在控制器中,我们通常使用 view() 方法返回一个视图文件. <?php namespace App\Http\Con ...
- go-goroutine 和 channel
goroutine 和 channel goroutine-看一个需求 需求:要求统计 1-9000000000 的数字中,哪些是素数? 分析思路: 1) 传统的方法,就是使用一个循环,循环的判断各个 ...
- java基础(12):构造方法、this、super
1. 构造方法 我们对封装已经有了基本的了解,接下来我们来看一个新的问题,依然以Person为例,由于Person中的属性都被private了,外界无法直接访问属性,必须对外提供相应的set和get方 ...
- JDK1.8新特性——Collector接口和Collectors工具类
JDK1.8新特性——Collector接口和Collectors工具类 摘要:本文主要学习了在Java1.8中新增的Collector接口和Collectors工具类,以及使用它们在处理集合时的改进 ...
- DataGridView中的rows.Count比实际行数多1的原因以及解决办法
场景 DataGridView怎样实现添加.删除.上移.下移一行: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/10281414 ...
- VS中怎样对C#项目进行单元测试
场景 SpringBoot+Junit在IDEA中实现查询数据库的单元测试: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/927 ...
- CSS3 2D变形 transform---移动 translate(x, y), 缩放 scale(x, y), 旋转 rotate(deg), transform-origin, 倾斜 skew(deg, deg)
transform是CSS3中具有颠覆性的特征之一,可以实现元素的位移.旋转.倾斜.缩放,甚至支持矩阵方式,配合过渡和即将学习的动画知识,可以取代大量之前只能靠Flash才可以实现的效果. 变形转换 ...
- 2019年上半年收集到的人工智能GAN干货文章
2019年上半年收集到的人工智能GAN干货文章 GAN简介及其常见应用 训练GAN,你应该知道的二三事 了解生成对抗网络(GAN) CosmoGAN:训练GAN,让AI寻找宇宙中的暗物质 关于GAN的 ...
- sqlserver查询是否阻塞
查询当前正在执行的语句 SELECT der.[session_id],der.[blocking_session_id], sp.lastwaittype,sp.hostname,sp.progra ...
- Flask Rest接口
Flask适用于简单的接口请求 安装 pip install Flask pip install Flask-RESTful 仅简单请求url,然后出发处理程序,返回处理结果 app.py代码如下 f ...