Pause loop until download event is complete - c#

How can I pause the loop in someRandomMethod() until the code in DownloadCompleted() have been executed? This code below only unpacks the latest version in the versions array. It's like the loop is faster than the first download and m_CurrentlyDownloading have the latest value the first time DownloadCompleted() is beeing executed.
private void someRandomMethod() {
for (int i = 0; i < versions.Count; i++)
{
//ClearInstallFolder();
m_CurrentlyDownloading = versions.ElementAt(i);
Download(versions.ElementAt(i));
LocalUpdate(versions.ElementAt(i));
System.Threading.Thread.Sleep(500);
}
}
private void Download(string p_Version)
{
string file = p_Version + ".zip";
string url = #"http://192.168.56.5/" + file;
//client is global in the class
client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressChanged);
client.DownloadFileAsync(new Uri(url), #"C:\tmp\" + file);
}
private void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Error == null)
{
Unpack(m_CurrentlyDownloading);
if (GetInstalledVersion() == GetLatestVersion())
ClearZipFiles();
}
else
MessageBox.Show(e.Error.ToString());
}

The easiest way would be to not use the *async methods. The normal DownloadFile will pause execution until it completes.
But if you've got access to the Await keyword, try this.
private async Task Download(string p_Version)
{
string file = p_Version + ".zip";
string url = #"http://192.168.56.5/" + file;
//client is global in the class
client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressChanged);
await client.DownloadFileAsync(new Uri(url), #"C:\tmp\" + file);
}

something like this can be used to wait
make it class property
bool IsDownloadCompleted=false;
Add this in DownloadCompletedEvent
IsDownloadCompleted=true;
and this where you want to stop loop
while(DownloadCompleted!=true)
{
Application.DoEvents();
}

Create some boolean variable, create a delegate and get\set methods for this variable.
Then just in loop made smth like :
while(!isDownLoadCompleted)Thread.Sleep(1024);

You Can use Paralel.ForEach. this loop will wait until all threads done.
check Here for how to use :
http://msdn.microsoft.com/tr-tr/library/dd460720(v=vs.110).aspx
or
http://blogs.msdn.com/b/pfxteam/archive/2012/03/05/10278165.aspx

Related

Pass parameters to WebClient.DownloadFileCompleted event

I am using the WebClient.DownloadFileAsync() method, and wanted to know how can i pass a parameter to the WebClient.DownloadFileCompleted event (or any other event for that matter), and use it in the invoked method.
My code:
public class MyClass
{
string downloadPath = "some_path";
void DownloadFile()
{
int fileNameID = 10;
WebClient webClient = new WebClient();
webClient.DownloadFileCompleted += DoSomethingOnFinish;
Uri uri = new Uri(downloadPath + "\" + fileNameID );
webClient.DownloadFileAsync(uri,ApplicationSettings.GetBaseFilesPath +"\" + fileNameID);
}
void DoSomethingOnFinish(object sender, AsyncCompletedEventArgs e)
{
//How can i use fileNameID's value here?
}
}
How can I pass a parameter to DoSomethingOnFinish()?
You can use webClient.QueryString.Add("FileName", YourFileNameID); to add extra information.
Then to access it in your DoSomethingOnFinish function,
use string myFileNameID = ((System.Net.WebClient)(sender)).QueryString["FileName"]; to receive the file name.
This is what the code should look like:
string downloadPath = "some_path";
void DownloadFile()
{
int fileNameID = 10;
WebClient webClient = new WebClient();
webClient.DownloadFileCompleted += new AsyncCompletedEventHandler(DoSomethingOnFinish);
webClient.QueryString.Add("fileName", fileNameID.ToString());
Uri uri = new Uri(downloadPath + "\\" + fileNameID);
webClient.DownloadFileAsync(uri,ApplicationSettings.GetBaseFilesPath +"\\" + fileNameID);
}
void DoSomethingOnFinish(object sender, AsyncCompletedEventArgs e)
{
//How can i use fileNameID's value here?
string myFileNameID = ((System.Net.WebClient)(sender)).QueryString["fileName"];
}
Even if this should work, you should be using Unity's UnityWebRequest class. You probably haven't heard about it but this is what it should look like:
void DownloadFile(string url)
{
StartCoroutine(downloadFileCOR(url));
}
IEnumerator downloadFileCOR(string url)
{
UnityWebRequest www = UnityWebRequest.Get(url);
yield return www.Send();
if (www.isError)
{
Debug.Log(www.error);
}
else
{
Debug.Log("File Downloaded: " + www.downloadHandler.text);
// Or retrieve results as binary data
byte[] results = www.downloadHandler.data;
}
}

ProgressBar progress change with all files that need to be downloaded

I'm making a WPF application where I use WebClient to download files form a webserver. I have a list of URL's with all the files i have to download. I use a foreach to loop through every URL and download each one at the time. The first URL much be completed before moving to the next one. I know the size of each file. Is there a way where I can set my e.ProgressPercentage to know the size of all files instead of loading from 0 to 100% for each file. I know that I'm calling DownloadProtocol for each URL right now, which makes a new instance of WebClient, but it is the only way I can think of to fulfill my solution, which is to download one file at a time.
public DownloadStart()
{
foreach(var url in ListOfDownloadURL)
{
DownloadGameFile dlg = new DownloadGameFile();
await dlg.DownloadProtocol(url, myLocation);
}
}
Download function in DownloadGameFile class:
public async Task DownloadProtocol(string address, string location)
{
Uri Uri = new Uri(address);
using (WebClient client = new WebClient())
{
//client.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
//client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgress);
client.DownloadProgressChanged += (o, e) =>
{
Console.WriteLine(e.BytesReceived + " " + e.ProgressPercentage);
//ProgressBar = e.ProgressPercentage (total)
};
client.DownloadFileCompleted += (o, e) =>
{
if (e.Cancelled == true)
{
Console.WriteLine("Download has been canceled.");
}
else
{
Console.WriteLine("Download completed!");
}
};
await client.DownloadFileTaskAsync(Uri, location);
}
}
Why not take the easy way out and just update the progress when file is completed? Something like...
ProgressBar p = new ProgressBar();
p.Maximum = ListOfDownloadURL.Count();
foreach(var url in ListOfDownloadURL)
{
DownloadGameFile dlg = new DownloadGameFile();
await dlg.DownloadProtocol(url, myLocation);
p.Value += 1;
}
Or if you insist, you could query file sizes before you begin downloading, sum total bytes of all the files and then calculate the percentage when ever DownloadProgressChanged is fired.
var bytes = Convert.ToInt64(client.ResponseHeaders["Content-Length"]);

Get progress of an async call

I'm using a block of code I got from a blog, to upload images to IMGur using API v3.
It works fine, but I wanted to implement a progress bar system to let the user know how much has been uploaded, if the program deals with high res images.
So far I haven't been able to do so.
I'm not an experienced coder, just doing this as a learning project.
The Code:
public object UploadImage(string image)
{
WebClient w = new WebClient();
w.UploadProgressChanged += (s, e) => { };
w.UploadValuesCompleted += (s, e) => { };
w.Headers.Add("Authorization", "Client-ID " + ClientId);
System.Collections.Specialized.NameValueCollection Keys = new System.Collections.Specialized.NameValueCollection();
try
{
Keys.Add("image", Convert.ToBase64String(File.ReadAllBytes(image)));
byte[] responseArray = w.UploadValues("https://api.imgur.com/3/image", Keys);
dynamic result = Encoding.ASCII.GetString(responseArray); System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("link\":\"(.*?)\"");
System.Text.RegularExpressions.Match match = reg.Match(result);
string url = match.ToString().Replace("link\":\"", "").Replace("\"", "").Replace("\\/", "/");
textBox1.Text = url;
return url;
}
catch (Exception s)
{
MessageBox.Show("Something went wrong. " + s.Message);
return "Failed!";
}
}
At first I tried using the events UploadProgressChanged and UploadValuesCompleted, but they are not triggered, my theory is they are triggered when UploadValuesAsync is called instead of UploadValues.
How do I implement a progress system?
What is the difference between async and normal transfer?
The difference between aync and normal transfer is, that the UploadValues method will block the current thread until all data has been transferred. Because the thread is blocked in this time you can't catch any events too. Therefore you have to use the asynchrony method UploadValuesAsync which will transfer the data in the background and you're able to go on with the execution of your code.
The UploadProgressChanged only fires for the UploadValuesAsync too. Your code should look something like this (Not tested!):
public String UploadImage(string image)
{
WebClient w = new WebClient();
w.UploadProgressChanged += (s, e) =>
{
myProgressBar.Maximum = (int)e.TotalBytesToSend;
myProgressBar.Value = (int)e.BytesSent;
};
w.UploadValuesCompleted += new UploadValuesCompletedEventHandler(UploadComplete);
w.Headers.Add("Authorization", "Client-ID " + ClientId);
System.Collections.Specialized.NameValueCollection Keys = new System.Collections.Specialized.NameValueCollection();
try
{
Keys.Add("image", Convert.ToBase64String(File.ReadAllBytes(image)));
w.UploadValuesAsync("https://api.imgur.com/3/image", Keys);
return "Uploading..";
} catch (Exception s)
{
MessageBox.Show("Something went wrong. " + s.Message);
return "Failed!";
}
}
public void UploadComplete(Object sender, UploadValuesCompletedEventArgs e)
{
myProgressBar.Value = 100;
byte[] responseArray = e.Result;
dynamic result = Encoding.ASCII.GetString(responseArray);
System.Text.RegularExpressions.Regex reg = new System.Text.RegularExpressions.Regex("link\":\"(.*?)\"");
System.Text.RegularExpressions.Match match = reg.Match(result);
string url = match.ToString().Replace("link\":\"", "").Replace("\"", "").Replace("\\/", "/");
textBox1.Text = url;
}
Edit
I moved the code after the UploadValuesAsync call into the w.UploadValuesCompleted. You can find the server response in the Result field of the UploadValuesCompletedEventArgs class which is passed to the event in the variable e.
Your method UploadImage will now return Uploading when the progress started and you'll have to do your rest work in the w.UploadValuesCompleted event.

UploadFileAsync with Foreach

I have:
foreach (FileInfo fileinfo in Arquivos)
{
float zz = (float)fileinfo.Length;
zz = (zz / 1024f) / 1024f;
label8.Text = "sending: " + fileinfo.Name + "("+zz.ToString("0.0")+"MB)...";
label8.Update();
WebClient client = new System.Net.WebClient();
client.Credentials = new System.Net.NetworkCredential(usuario, senha);
client.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadProgressCallback);
client.UploadFile(new Uri("ftp://" + ftp + "/" + caminho + "//" + fileinfo.Name), "STOR", pasta + mes + fileinfo.Name);
bar++;
backgroundWorker1.ReportProgress(bar);
}
And I need create a UploadProgressChanged, so I have:
client.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadProgressCallback);
And
private void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
{
progressBar2.Value = e.ProgressPercentage;
progressBar2.Update();
}
UploadProgressChanged not work with UploadFile, just UploadfileAsync, but I need send 1 file per time. How can I change UploadFile to UploadFileAsync and send a file per time?
If you're only able to send 1 file at a time then why are you concerned about using Async? You also seem to be using a background worker to do all the work.
Might you not be better off spinning up a 'Task' for each file to be uploaded, and kicking them off with a scheduler that only allows one task at a time?
see: http://msdn.microsoft.com/en-us/library/ee789351.aspx
That way you could make use of some the newer Task and async methods available while simplifying your task.
Based on further analysis if you want to run them asynchronously but only do one at a time:
private AutoResetEvent _fileUploadedEvent = new AutoResetEvent(false);
private void DoUploadBackgroundWorker()
{
foreach (var file in files)
{
client.WhenUploaded += (s, e) =>
{
// This signals the AutoResetEvent that it can continue
_fileUploadedEvent.Set();
};
client.UploadAsync();
// This will keep ticking over every 15 milliseconds to check if the
// AutoResetEvent has been triggered
while (_fileUploadedEvent.WaitOne(15)) { }
// We get here when it's been triggered (which means the file was uploaded)
// So we can update the progressbar here and then move onto the next file.
}
}
It needs expanding and the classes aren't all correct as I've just knocked this together, but it should provide enough material to start you in the direction.

DownloadFileCompleted event doesn't fire inside while loop c# .net

I'm a bit confused why the event doesn't fire when the file has downloaded.
The file it self downloads perfectly fine.
I'm assuming there is some error in the way I am using this, in that the event doesn't fire inside a loop.
Thanks for any help anyone can give me
class DownloadQueue
{
public List<string[]> DownloadItems { get; set; }
public int CurrentDownloads;
public int DownloadInProgress;
string url = #"http://www.google.co.uk/intl/en_uk/images/logo.gif";
bool downloadComplete;
public DownloadQueue()
{
CurrentDownloads = 0;
DownloadItems = new List<string[]>();
Console.Write("new download queue made");
}
public void startDownloading(int maxSimulatiousDownloads)
{
downloadComplete = true;
DownloadInProgress = 0;
WebClient client = new WebClient();
client.DownloadFileCompleted +=
new AsyncCompletedEventHandler(this.downloadCompleteMethod);
while(DownloadInProgress != DownloadItems.Count )
{
if (downloadComplete == true)
{
downloadComplete = false;
client.DownloadFileAsync(new Uri(DownloadItems.ElementAt(DownloadInProgress).ElementAt(0).ToString()), DownloadItems.ElementAt(DownloadInProgress).ElementAt(1).ToString());
}
}
Console.Write("all downloads completed");
}
private void downloadCompleteMethod(object sender, AsyncCompletedEventArgs e)
{
downloadComplete = true;
DownloadInProgress++;
Console.Write("file Downloaded");
}
}
Where are you waiting for the DownloadFileAsync() call to complete? How about something like
manualResetEvent AllDone = new mre(false)
just before console.WriteLine
AllDone.WaitOne()
And in download complete
if (interlocked.decrement(ref downloadComplete) == 0) { AllDone.Set(); }
Most likely there is no time for the message to come. I suspect if you put an Application.DoEvents at the end of your loop, it would start firing the events. It is less than ideal to use it, but with the design you have, I can't think of a better way.

Categories

Resources