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
Visualizing an Ast Created with Antlr (In a .Net Environment)
How to Make Correct Date Format When Writing Data to Excel
Thread with Multiple Parameters
Should You Access a Variable Within the Same Class via a Property
How to Implement the Sieve of Eratosthenes Using Multithreaded C#
How to Find the Position of a Cursor in a Text Box? C#
Error Deserializing Xml to Object - Xmlns='' Was Not Expected
Using Folderbrowserdialog in Wpf Application
"Padding Is Invalid and Cannot Be Removed" Using Aesmanaged
Why Does the Async Keyword Exist
The 'Await' Operator Can Only Be Used Within an Async Lambda Expression
Convert.Toint32() a String with Commas
The Remote Server Returned an Unexpected Response: (413) Request Entity Too Large
Download Feature Not Working Within Update Panel in ASP.NET
What Is the Correct Way to Cancel an Async Operation That Doesn't Accept a Cancellationtoken