代码改变世界

一个简单的利用 WebClient 异步下载的示例(二)

2018-09-11 14:59  音乐让我说  阅读(297)  评论(0编辑  收藏  举报

继上一篇 一个简单的利用 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分钟),那么整体的下载进度就会变慢。接下来,我会逐步优化它,敬请关注!

谢谢浏览!