【问题标题】:How do I Async download multiple files using webclient, but one at a time?如何使用 webclient 异步下载多个文件,但一次下载一个?
【发布时间】:2011-08-09 07:08:07
【问题描述】:

很难找到使用 webclient 类异步方法下载多个文件但一次下载一个文件的代码示例。

如何启动异步下载,但要等到第一个完成后再等到第二个,等等。基本上是一个问题。

(注意我不想使用同步方法,因为异步方法增加了功能。)

下面的代码会立即开始我的所有下载。 (进度条到处都是)

private void downloadFile(string url)
        {
            WebClient client = new WebClient();

            client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
            client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);

            // Starts the download
            btnGetDownload.Text = "Downloading...";
            btnGetDownload.Enabled = false;
            progressBar1.Visible = true;
            lblFileName.Text = url;
            lblFileName.Visible = true;
            string FileName = url.Substring(url.LastIndexOf("/") + 1,
                            (url.Length - url.LastIndexOf("/") - 1));
             client.DownloadFileAsync(new Uri(url), "C:\\Test4\\" + FileName);

        }

        void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {

        }

        void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            double bytesIn = double.Parse(e.BytesReceived.ToString());
            double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
            double percentage = bytesIn / totalBytes * 100;
            progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
        }

【问题讨论】:

标签: c# asynchronous webclient


【解决方案1】:

我所做的是填充一个包含我所有网址的队列,然后我下载队列中的每个项目。当没有剩余物品时,我可以处理所有物品。我在下面模拟了一些代码。请记住,下面的代码用于下载字符串而不是文件。修改下面的代码应该不会太难。

    private Queue<string> _items = new Queue<string>();
    private List<string> _results = new List<string>();

    private void PopulateItemsQueue()
    {
        _items.Enqueue("some_url_here");
        _items.Enqueue("perhaps_another_here");
        _items.Enqueue("and_a_third_item_as_well");

        DownloadItem();
    }

    private void DownloadItem()
    {
        if (_items.Any())
        {
            var nextItem = _items.Dequeue();

            var webClient = new WebClient();
            webClient.DownloadStringCompleted += OnGetDownloadedStringCompleted;
            webClient.DownloadStringAsync(new Uri(nextItem));
            return;
        }

        ProcessResults(_results);
    }

    private void OnGetDownloadedStringCompleted(object sender, DownloadStringCompletedEventArgs e)
    {
        if (e.Error == null && !string.IsNullOrEmpty(e.Result))
        {
            // do something with e.Result string.
            _results.Add(e.Result);
        }
        DownloadItem();
    }

编辑: 我已修改您的代码以使用队列。不完全确定您希望如何取得进展。我确定如果您希望进度满足所有下载,那么您可以将项目计数存储在“PopulateItemsQueue()”方法中,并在进度更改方法中使用该字段。

    private Queue<string> _downloadUrls = new Queue<string>();

    private void downloadFile(IEnumerable<string> urls)
    {
        foreach (var url in urls)
        {
            _downloadUrls.Enqueue(url);
        }

        // Starts the download
        btnGetDownload.Text = "Downloading...";
        btnGetDownload.Enabled = false;
        progressBar1.Visible = true;
        lblFileName.Visible = true;

        DownloadFile();
    }

    private void DownloadFile()
    {
        if (_downloadUrls.Any())
        {
            WebClient client = new WebClient();
            client.DownloadProgressChanged += client_DownloadProgressChanged;
            client.DownloadFileCompleted += client_DownloadFileCompleted;

            var url = _downloadUrls.Dequeue();
            string FileName = url.Substring(url.LastIndexOf("/") + 1,
                        (url.Length - url.LastIndexOf("/") - 1));

            client.DownloadFileAsync(new Uri(url), "C:\\Test4\\" + FileName);
            lblFileName.Text = url;
            return;
        }

        // End of the download
        btnGetDownload.Text = "Download Complete";
    }

    private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
    {
        if (e.Error != null)
        {
            // handle error scenario
            throw e.Error;
        }
        if (e.Cancelled)
        {
            // handle cancelled scenario
        }
        DownloadFile();
    }

    void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        double bytesIn = double.Parse(e.BytesReceived.ToString());
        double totalBytes = double.Parse(e.TotalBytesToReceive.ToString());
        double percentage = bytesIn / totalBytes * 100;
        progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
    }

【讨论】:

  • 如果下载失败(或由于任何原因未完成)会发生什么情况,那么队列的其余部分也不会被下载,因为 client_DownloadFileCompleted 永远不会触发对吗?有没有办法检查这个,超时功能或什么?
  • @Dan:如果发生错误,可以通过检查 AsyncCompletedEventArgs 参数的 Error 属性在 client_DownloadFileCompleted 方法中检测到。我已经更新了代码以演示可以在哪里/如何完成。
  • WebClient 不是一次性的吗?
  • 对于每个下载的自定义FileName(列表或数组字符串)怎么样?我也应该排队吗?
【解决方案2】:

我很难理解问题出在哪里。如果您只为第一个文件调用异步方法,它不会只下载该文件吗?为什么不使用 client_downlaodFileCompleted 事件根据 AsyncCompletedEvents 传递的某个值启动下一个文件下载,或者将下载的文件列表维护为静态变量并让 client_DownloadFileCompleted 迭代该列表以查找下一个要下载的文件。

希望对您有所帮助,但如果我误解了您的问题,请发布更多信息。

【讨论】:

    【解决方案3】:

    我会创建一个新方法,例如名为 getUrlFromQueue 的方法,它会从队列(集合或数组)中返回一个新的 url 并将其删除。然后它调用 downloadFile(url) - 在 client_DownloadFileCompleted 中我再次调用 getUrlFromQueue。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多