继上一篇 一个简单的利用 WebClient 异步下载的示例(一) 后,我想把核心的处理提取出来,成 SkyWebClient,如下:
1. SkyWebClient
该构造函数中 downloadConfigs 参数是必须的,不能为 NULL,而 ProgressBar progressBar 可为空,只不过不能显示进度条而已。
public class DownloadEntry { public string Url { get; set; } public string Path { get; set; } /// <summary> /// 当前处理的数据 /// </summary> public object Data { get; set; } public DownloadEntry(string url, string savedPath) :this(url,savedPath, null) { } public DownloadEntry(string url, string savedPath, object data) { Url = url; Path = savedPath; Data = data; } } /// <summary> /// 当单个下载前的事件处理 /// </summary> /// <param name="current">当前处理的数据,有可能为 NULL,请注意判断</param> public delegate void WhenSingleDownloadingEventHandler(DownloadEntry current); /// <summary> /// 当全部下载完毕后的事件处理 /// </summary> public delegate void WhenAllDownloadedEventHandler(); /// <summary> /// 当下载错误时 /// </summary> /// <param name="ex"></param> public delegate void WhenDownloadingErrorEventHandler(Exception ex); public class SkyWebClient : INotifyPropertyChanged { #region 字段、属性 public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged(string prop) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); } } /// <summary> /// 当单个下载前的事件处理 /// </summary> public event WhenSingleDownloadingEventHandler WhenSingleDownloading; private void OnWhenSingleDownloading(DownloadEntry next) { if (WhenSingleDownloading != null) { WhenSingleDownloading(next); } } /// <summary> /// 当全部下载完毕后的事件处理 /// </summary> public event WhenAllDownloadedEventHandler WhenAllDownloaded; private void OnWhenAllDownloaded() { if (WhenAllDownloaded != null) { WhenAllDownloaded(); } } /// <summary> /// 当全部下载完毕后的事件处理 /// </summary> public event WhenDownloadingErrorEventHandler WhenDownloadingError; private void OnWhenDownloadingError(Exception ex) { if (WhenDownloadingError != null) { WhenDownloadingError(ex); } } ProgressBar progressBar; WebClient webc; Queue<DownloadEntry> ToDownload = new Queue<DownloadEntry>(); bool _canChange = true; public bool CanChange { get { return _canChange; } set { _canChange = value; OnPropertyChanged("CanChange"); } } #endregion public SkyWebClient(IEnumerable<DownloadEntry> downloadConfigs) :this(downloadConfigs, null) { } public SkyWebClient(IEnumerable<DownloadEntry> downloadConfigs, ProgressBar progressBar) { if (downloadConfigs == null) { throw new ArgumentNullException("downloadConfigs"); } foreach (DownloadEntry item in downloadConfigs) { ToDownload.Enqueue(item); } this.webc = new WebClient(); this.progressBar = progressBar; webc.DownloadFileCompleted += Webc_DownloadFileCompleted; //注册下载完成事件 webc.DownloadProgressChanged += Webc_DownloadProgressChanged; //注册下载进度改变事件 } public void Start() { if (ToDownload.Count == 0) { Downloaded(); return; } if (CanChange) { CanChange = false; if (this.progressBar != null) { this.progressBar.Maximum = ToDownload.Count * 100; } StartCore(); } else { ToDownload.Clear(); webc.CancelAsync(); CanChange = true; //this.progressBar.Value = 0; //btnRunByWebClient.Text = "用 WebClient 开始下载"; } } private void StartCore() { DownloadEntry next = ToDownload.Dequeue(); webc.DownloadFileAsync(new Uri(next.Url), next.Path); OnWhenSingleDownloading(next); } private void DownloadNext() { if (ToDownload.Any()) { StartCore(); if (this.progressBar != null) { this.progressBar.Value = this.progressBar.Maximum - ((ToDownload.Count + 1) * 100); } } else { if (this.progressBar != null) { this.progressBar.Value = 0; } Downloaded(); } } private void Downloaded() { CanChange = true; OnWhenAllDownloaded(); } private void Webc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) { if (e.Error != null && !e.Cancelled) { OnWhenDownloadingError(e.Error); CanChange = true; //this.progressBar.Value = 0; } else { DownloadNext(); } } private void Webc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e) { if (this.progressBar != null) { this.progressBar.Value = this.progressBar.Maximum - ((ToDownload.Count + 1) * 100) + e.ProgressPercentage; } } }
2. Form1.cs
在点击事件中,调用 SkyWebClient。
public partial class Form1 : Form { public Form1() { InitializeComponent(); } private List<int> GetDownloadIds() { List<int> ids = new List<int>(100); for (int i = 1; i <= 100; 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 WhenSingleDownloaded(int id, bool singleDownloadSuccess) { this.listBoxLog.Items.Insert(0, string.Format("当前时间:{0},编号 {1} 下载 {2}!", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), id, singleDownloadSuccess)); } 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); WhenSingleDownloaded(id, singleDownloadSuccess); } 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)); } SkyWebClient skyWebClient = new SkyWebClient(downloadConfigs, this.progressBar1); skyWebClient.WhenAllDownloaded += SkyWebClient_WhenAllDownloaded; skyWebClient.WhenSingleDownloading += SkyWebClient_WhenSingleDownloading; skyWebClient.WhenDownloadingError += SkyWebClient_WhenDownloadingError; skyWebClient.Start(); } private void SkyWebClient_WhenDownloadingError(Exception ex) { MessageBox.Show("下载时出现错误: " + ex.Message); } private void SkyWebClient_WhenSingleDownloading(DownloadEntry current) { WhenSingleDownloaded((int)current.Data, true); } private void SkyWebClient_WhenAllDownloaded() { btnRunByWebClient.Text = "用 WebClient 开始下载"; WhenAllDownloaded(); } }
3. 运行截图
如图:
4. 总结
由于本下载采用依次来异步下载,虽然可以保证下载的文件的完整,但单位时间内效率不高,可能进度缓慢。且如果其中一个占用了较多的时间(比如:2分钟),那么整体的下载进度就会变慢。接下来,我会逐步优化它,敬请关注!
谢谢浏览!