web-dev-qa-db-fra.com

Comment puis-je télécharger Async plusieurs fichiers en utilisant Webclient, mais un à la fois?

Il a été étonnamment difficile de trouver un exemple de code de téléchargement de plusieurs fichiers à l'aide de la méthode asynchrone de la classe webclient, mais en téléchargeant un à la fois.

Comment puis-je lancer un téléchargement asynchrone, mais attendez que le premier soit terminé jusqu'au second, etc. Fondamentalement, c'est un que.

(notez que je ne veux pas utiliser la méthode de synchronisation, en raison de la fonctionnalité accrue de la méthode async.)

Le code ci-dessous démarre tous mes téléchargements en même temps. (la barre de progression est partout)

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

Ce que j'ai fait, c'est remplir une file d'attente contenant toutes mes URL, puis je télécharge chaque élément de la file d'attente. Lorsqu'il n'y a plus d'articles, je peux alors traiter tous les articles. J'ai raillé du code ci-dessous. Gardez à l'esprit que le code ci-dessous permet de télécharger des chaînes et non des fichiers. Il ne devrait pas être trop difficile de modifier le code ci-dessous.

    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: J'ai modifié votre code pour utiliser une file d'attente. Je ne sais pas exactement comment vous souhaitiez que le progrès fonctionne. Je suis sûr que si vous vouliez que la progression couvre tous les téléchargements, vous pouvez stocker le nombre d'éléments dans la méthode 'PopulateItemsQueue ()' et utiliser ce champ dans la méthode de progression modifiée.

    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

J'ai du mal à comprendre où est le problème. Si vous appelez uniquement la méthode async pour le premier fichier, ne téléchargera-t-il pas uniquement ce fichier? Pourquoi ne pas utiliser l'événement client_downlaodFileCompleted pour lancer le prochain téléchargement de fichier en fonction d'une valeur transmise par AsyncCompletedEvents, ou maintenir une liste de fichiers téléchargés en tant que variable statique et demander à client_DownloadFileCompleted d'itérer la liste pour trouver le fichier suivant à télécharger.

J'espère que cela aide, mais veuillez publier plus d'informations si j'ai mal compris votre question.

2
Jason James

Je ferais une nouvelle méthode, par exemple nommée getUrlFromQueue qui me renvoie une nouvelle URL de la file d'attente (collection ou tableau) et la supprime .. puis elle appelle downloadFile (url) - et dans client_DownloadFileCompleted j'appelle à nouveau getUrlFromQueue.

1
MilMike