简体   繁体   English

如何使用webclient异步下载多个文件,但一次只能一个?

[英]How do I Async download multiple files using webclient, but one at a time?

It has been surprisingly hard to find a code example of downloading multiple files using the webclient class asynchronous method, but downloading one at a time. 很难找到使用webclient类异步方法下载多个文件的代码示例,但一次下载一个。

How can I initiate a async download, but wait until the first is finished until the second, etc. Basically a que. 如何启动异步下载,但要等到第一个完成,直到第二个,等等。基本上是一个问题。

(note I do not want to use the sync method, because of the increased functionality of the async method.) (注意我不想使用同步方法,因为异步方法的功能增加了。)

The below code starts all my downloads at once. 以下代码立即启动我的所有下载。 (the progress bar is all over the place) (进度条到处都是)

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());
        }

What I have done is populate a Queue containing all my urls, then I download each item in the queue. 我所做的是填充一个包含我所有网址的队列,然后我下载队列中的每个项目。 When there are no items left, I can then process all the items. 当没有剩余物品时,我可以处理所有物品。 I've mocked some code up below. 我在下面嘲笑了一些代码。 Keep in mind, the code below is for downloading strings and not files. 请记住,下面的代码用于下载字符串而不是文件。 It shouldn't be too difficult to modify the below code. 修改下面的代码应该不会太难。

    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();
    }

Edit: I've modified your code to use a Queue. 编辑:我已修改您的代码以使用队列。 Not entirely sure how you wanted progress to work. 不完全确定你希望如何进步。 I'm sure if you wanted the progress to cater for all downloads, then you could store the item count in the 'PopulateItemsQueue()' method and use that field in the progress changed method. 我确定您是否希望进度能够满足所有下载,然后您可以将项目计数存储在'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());
    }

I am struggling to understand where the issue is. 我很难理解问题所在。 If you only call the async method for the first file, won't it only download that file? 如果你只为第一个文件调用异步方法,它不会只下载该文件吗? Why not use the client_downlaodFileCompleted event to initiate the next file download based on some value passed by AsyncCompletedEvents, or maintain a list of downloaded files as a static variable and have client_DownloadFileCompleted iterate tht list to find the next file to download. 为什么不使用client_downlaodFileCompleted事件根据AsyncCompletedEvents传递的某个值启动下一个文件下载,或者将下载的文件列表保存为静态变量,并使用client_DownloadFileCompleted迭代列表以查找要下载的下一个文件。

Hope that helps but please post more information if I have miunderstood your question. 希望有所帮助,但如果我误解了您的问题,请发布更多信息。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM