Download Multiple Files Async and Wait for All of Them to Finish Before Executing the Rest of the Code

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.

How to wait for results from multiple async methods wrapped in a for loop?

I think I know what you try to achieve, and the issue you are having, this.getFinalDisplayResults() is executed before you have the results because the logic inside the for loop is asynchronous, so the fix would be.

async function getDataFromBackend () {
for(let type in ["player", "team", "event"]) {
const searchResult = await this.searchService.getSearchResult(type).toPromise()
if(type === "player")
this.player_search_results_full = this.getPlayerSearchResults(searchResult, search_string);

if(type === "team")
this.team_search_results_full = this.getTeamSearchResults(searchResult, search_string);

if(type === "event")
this.event_search_results_full = this.getEventSearchResults(searchResult, search_string);
}
}



async function getFinalDisplayResults() {
await getDataFromBackend(); // this will ensure you have the data, before you do the rest of the process
//rest of the logic here
}

C# Having Trouble Asynchronously Downloading Multiple Files in Parallel on Console Application

async void is your problem

Change

public static async void DownloadAllFiles(Data clientData)

To

public static async Task DownloadAllFiles(Data clientData)

Then you can await it

await FileDownloader.DownloadAllFiles(data);

The longer story:

async void runs unobserved (fire and forget). You can't wait for them to finish. In essence as soon as your program starts the task, it finishes, and tears down the App Domain and all your sub tasks, leading you to believe nothing is working.

Download Multiple Files With Same Authenticated Session WebClient

There's a per host per process connection limit.

Try setting ServicePoint.ConnectionLimit or ServicePointManager.DefaultConnectionLimit.

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

How do I get my C# Async and Wait code to work?

You have to use the event WebClient.DownloadFileCompleted Event which will be raised when the file is completely downloaded, then execute whatever code you want on the file.

So you need to register the event for your webClient.
like :

client.DownloadFileCompleted += wc_DownloadFileCompleted;

Then call your code to extract and execute in the DownloadFileCompleted event.

private static void wc_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
{
String ZipPath = Environment.CurrentDirectory + "\\spacelightzipped.zip";
String extractPath = Environment.CurrentDirectory;
ZipFile.ExtractToDirectory(ZipPath, extractPath);

System.Diagnostics.Process proc = new System.Diagnostics.Process
{
EnableRaisingEvents = false
};
proc.StartInfo.FileName = Environment.CurrentDirectory + "\\SpaceLightApp.exe";
proc.Start();
}


Related Topics



Leave a reply



Submit