一个简单的利用 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)创建一个简单的查询实例(二) 以上两篇已经把流 ...
随机推荐
- js实现图片无缝循环跑马灯
html 代码 <div class="myls-out-div" style="overflow: hidden;"> <ul id=&qu ...
- Zuul 1.x 的工作原理
Zuul简介 Zuul在微服务架构中,可以作为提供动态路由,监控,弹性,安全等边缘服务的框架.在Netflix,被用作所有请求到达streaming application的前门.Zuul使用一系列不 ...
- Javase之集合体系(2)之List及其子类ArrayList,LinkedList与Vector及其迭代器知识
集合体系之List及其子类ArrayList,LinkedList与Vector及其迭代器知识 List(接口) 特点:有序(存储与取出顺序相同),可重复 List子类特点: ArrayList: ...
- 使用element-ui的el-menu导航选中后刷新页面保持当前选中
<el-menu :default-active=‘$route.path‘ :router=‘true‘ :unique-opened=‘true‘ :default-openeds=&quo ...
- npm命令集合
1.npm初始化项目:npm init 或者 npm init --yes 生产 package.json文件 2.下载package.json安装包: npm install 缩写 npm i
- 如果下载老版本的Xcode
打开:https://developer.apple.com/download/more/,选择符合自己的Xcode版本即可.
- 【微信小程序】安装DingoApi开发小程序api
1.安装 composer require dingo/api 2.发布配置: php artisan vendor:publish 选择DingoApi发布. 3.向.env文件添加配置 (1)项目 ...
- Lnmp架构部署动态网站环境.2019-7-3-1.4
安装wiki开源产品 一.创建数据库 [root@Lnmp bbs]# mysql -uroot -p123456 mysql> create database wiki; #创建wiki数据库 ...
- Shell命令-搜索文件或目录之whereis、locate
文件及内容处理 - whereis.locate 1. whereis:查找二进制命令,按环境变量PATH路径查找 whereis命令的功能说明 whereis 命令用于查找文件.该指令会在特定目录中 ...
- python 绘制词云图
1. 先下载并安装nltk包,准备一张简单的图片存入代码所在文件目录,搜集英文停用词表 import nltk nltk.download() 2. 绘制词云图 import re import nu ...