How to Async Download Multiple Files Using Webclient, But One at a Time

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

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.

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

Download multiple files async and wait for all of them to finish before executing the rest of the code

The DownloadFileAsync/DownloadFileCompleted members of WebClient use the Event-based Asynchronous Pattern. If you want to use async and await, you should be using the Task-based Asynchronous Pattern.

In this case, you should use the DownloadFileTaskAsync member, as such:

private async Task DownloadFileAsync(DocumentObject doc)
{
try
{
using (WebClient webClient = new WebClient())
{
string downloadToDirectory = @Resources.defaultDirectory + doc.docName;
webClient.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials;
await webClient.DownloadFileTaskAsync(new Uri(doc.docUrl), @downloadToDirectory);

//Add them to the local
Context.listOfLocalDirectories.Add(downloadToDirectory);
}
}
catch (Exception)
{
Errors.printError("Failed to download File: " + doc.docName);
}
}

private async Task DownloadMultipleFilesAsync(List<DocumentObject> doclist)
{
await Task.WhenAll(doclist.Select(doc => DownloadFileAsync(doc)));
}

Please note that your Context.listOfLocalDirectories.Add and Errors.printError methods should be threadsafe.

Download Multiple Files With Same Authenticated Session WebClient

There's a per host per process connection limit.

Try setting ServicePoint.ConnectionLimit or ServicePointManager.DefaultConnectionLimit.

WebClient.DownloadFileAsync - Download files one at a time

To simplify the task you can create separated attachment list:

list = new List<Attachment>(wi.Attachments);

where list is private field with type List<Attachment>.
After this you should configure WebClient and start downloading of first file:

if (list.Count > 0) {
WebClient wc = new WebClient();
wc.Credentials = (ICredentials)netCred;
wc.DownloadFileCompleted += new AsyncCompletedEventHandler(wc_DownloadFileCompleted);
wc.DownloadFileAsync(list[0].Uri, @"C:\" + list[0].Name);
}

Your DownloadFileComplete handler should check if not all files already downloaded and call DownloadFileAsync again:

void wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e) {
// ... do something useful
list.RemoveAt(0);
if (list.Count > 0)
wc.DownloadFileAsync(list[0].Uri, @"C:\" + list[0].Name);
}

This code is not optimized solution. This is just idea.



Related Topics



Leave a reply



Submit