web-dev-qa-db-ja.com

一度に1つずつ、webclientを使用して複数のファイルを非同期でダウンロードするにはどうすればよいですか?

Webclientクラスの非同期メソッドを使用して複数のファイルをダウンロードするコード例を見つけるのは意外にも困難ですが、一度に1つずつダウンロードします。

非同期ダウンロードを開始する方法を教えてください。最初のダウンロードが完了するまで2番目のダウンロードまで待機します。基本的にはキューです。

(asyncメソッドの機能が向上したため、syncメソッドを使用したくないことに注意してください。)

以下のコードは、すべてのダウンロードを一度に開始します。 (プログレスバーはいたるところにあります)

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

私が行ったことは、すべてのURLを含むキューにデータを入力し、キュー内の各アイテムをダウンロードすることです。アイテムがなくなったら、すべてのアイテムを処理できます。以下のコードをモックアップしました。以下のコードは、ファイルではなく文字列をダウンロードするためのものです。以下のコードを変更することはそれほど難しくないはずです。

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

問題がどこにあるのか理解するのに苦労しています。最初のファイルに対して非同期メソッドのみを呼び出す場合、そのファイルをダウンロードするだけではありませんか? client_downlaodFileCompletedイベントを使用して、AsyncCompletedEventsによって渡されたいくつかの値に基づいて次のファイルのダウンロードを開始するか、ダウンロードしたファイルのリストを静的変数として保持し、client_DownloadFileCompletedにリストを繰り返してダウンロードする次のファイルを見つけさせてください。

それがお役に立てば幸いですが、あなたの質問を理解できたら、もっと詳しい情報を投稿してください。

2
Jason James

たとえば、キュ​​ー(コレクションまたは配列)から新しいURLを返し、それを削除するgetUrlFromQueueという名前の新しいメソッドを作成します。次に、downloadFile(url)を呼び出します。そしてclient_DownloadFileCompletedでもう一度getUrlFromQueueを呼び出します。

1
MilMike