C# - Download and Saving images to folder - c#

I'm trying to show a form that displays a label like "Updating, this window will close once update has finished" to download a few images files. I put this on my form's shown.
private void frmExtraUpdater_Shown(object sender, EventArgs e)
{
for (int i = 1; i < 8; i++)
{
string _EmoticonURL = String.Format("https://dl.dropboxusercontent.com/u/110636189/MapleEmoticons/f{0}.bmp", i);
WebRequest requestPic = WebRequest.Create(_EmoticonURL);
WebResponse responsePic = requestPic.GetResponse();
Image webImage = Image.FromStream(responsePic.GetResponseStream()); // Error
webImage.Save(Application.StartupPath + #"\Images\f" + i + ".bmp");
}
}
However.. once the form is shown, the label doesn't even show because it doesn't load it (It insantly downloades the images. I want it to show the label and only then start the download).
The other problem is that it throws "A generic error occurred in GDI+." on the webImgae.Save part for some reason.
Why's that?
Oh and.. if the folder "Images" does not exist, will it automatically create it?
Thanks!

Here is Jon Skeet suggestion:
System.Net.WebClient webClient = new System.Net.WebClient();
webClient.DownloadFile(String.Format("https://dl.dropboxusercontent.com/u/110636189/MapleEmoticons/f{0}.bmp", i), Application.StartupPath + #"\Images\f" + i + ".bmp");
Use a background worker to update any UI components
BackgroundWorker backTask = new BackgroundWorker();
public frmExtraUpdater()
{
backTask.DoWork += backTask_DoWork;
backTask.RunWorkerCompleted += backTask_RunWorkerCompleted;
}
private void frmExtraUpdater_Shown(object sender, EventArgs e)
{
yourLabel.Text = "Downloading";
backTask.RunWorkerAsync();
}
void backTask_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i < 8; i++)
{
System.Net.WebClient webClient = new System.Net.WebClient();
webClient.DownloadFile(String.Format("https://dl.dropboxusercontent.com/u/110636189/MapleEmoticons/f{0}.bmp", i), Application.StartupPath + #"\Images\f" + i + ".bmp");
backTask.ReportProgress(i * (100 / 8), String.Format("https://dl.dropboxusercontent.com/u/110636189/MapleEmoticons/f{0}.bmp", i));
}
}
void backTask_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
yourLabel.Text = "Downloading" + e.UserState.ToString();
}
void backTask_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.Close();
}

You should use an async method, with webrequest async api (if you work with .net 4.5 or sup) or download your images in a background thread to avoid ui thread blocking.
You can try that:
private void frmExtraUpdater_Shown(object sender, EventArgs e)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += (o, d) =>
{
saveImages();
};
worker.RunWorkerCompleted += (o, f) =>
{
this.Close();
};
worker.RunWorkerAsync();
}
private void saveImages()
{
for (int i = 1; i < 8; i++)
{
string _EmoticonURL = String.Format("https://dl.dropboxusercontent.com/u/110636189/MapleEmoticons/f{0}.bmp", i);
WebRequest requestPic = WebRequest.Create(_EmoticonURL);
WebResponse responsePic = requestPic.GetResponse();
Image webImage = Image.FromStream(responsePic.GetResponseStream()); // Error
webImage.Save(Application.StartupPath + #"\Images\f" + i + ".bmp");
}
}

Related

Why WinForm Gets Stuck with BackgroundWorker?

hi guys i tried to copy some files with this Code everything is good and the app will copy files but in copy progress i cant move my app or do anything
i tried to use thread but its not works i also use backgroundWorker but still nothing the only control that doesnt get stuck is progressBar its works fine here is my code :
public Form1()
{
InitializeComponent();
backgroundWorker1.Dispose();
backgroundWorker1.DoWork += BackgroundWorker_DoWork;
backgroundWorker1.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
backgroundWorker1.ProgressChanged += BackgroundWorker_ProgressChanged;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker2.DoWork += BackgroundWorker2_DoWork;
backgroundWorker2.WorkerReportsProgress = true;
}
private void BackgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
File.Copy(sourcePath, targetPath);
}
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < fileSize; i++)
{
int p = (i + 1) * 100 / Convert.ToInt32(fileSize);
backgroundWorker1.ReportProgress(p);
}
}
private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
lbProgress.Text = e.ProgressPercentage.ToString();
progressBar1.Value = e.ProgressPercentage;
}
private void btnTarget_Click(object sender, EventArgs e)
{
folderBrowser = new FolderBrowserDialog();
if (folderBrowser.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
targetPath += folderBrowser.SelectedPath + #"\" + fileName;
lbTarget.Text = targetPath;
}
}
private void btnSource_Click(object sender, EventArgs e)
{
op = new OpenFileDialog();
if (op.ShowDialog() == DialogResult.OK)
{
sourcePath += op.FileName;
lbSource.Text = sourcePath;
fileInfo = new FileInfo(sourcePath);
fileSize = fileInfo.Length / 1024;
fileName = fileInfo.Name;
MessageBox.Show(string.Format("File size is: {0} KB", fileSize));
}
}
private void btnCopy_Click(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
backgroundWorker2.RunWorkerAsync();
}
You're updating the progress bar faster than the UI can update, for every single byte of the file being copied in a tight loop. You're flooding the UI thread with pointless work.
Remove backgroundWorker1, it's not doing anything useful anyway. If you don't have a way to track the progress (which you don't with File.Copy), just use a progress bar without progress (set Style to Marquee).
For testing I created a simple winform application with a button, a label and a background worker and added the following corresponding events:
private void OnBackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
var worker = (BackgroundWorker)sender;
for (int i = 0; i < 10; i++)
{
Thread.Sleep(500);
worker.ReportProgress(i * 10);
}
}
private void OnBackgroundWorkerProgressChanged(object sender, ProgressChangedEventArgs e)
{
labelProgress.Text = e.ProgressPercentage.ToString();
}
private void OnBackgroundWorkerRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
labelProgress.Text = "Done";
}
private void OnButtonProgressClick(object sender, System.EventArgs e)
{
backgroundWorker.RunWorkerAsync();
}
Works as expected.
Could You try to update Your DoWork to this:
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
int mainProgress = 0;
for (int i = 0; i < fileSize; i++)
{
int calculatedProgress = (i + 1) * 100 / Convert.ToInt32(fileSize);
if(calculatedProgress > mainProgress)
{
mainProgress = calculatedProgress;
backgroundWorker1.ReportProgress(mainProgress);
}
}
}
maybe You are doing so many updates that simply Window Thread is all the time updating only progress and don't have a time to make anything else?

Updating progressbar through backgroundworker in method

My programm (in C# using Windows Forms) is reading and parsing large amounts of Data and I'm using a Backgroundworker which calls those global methods (reading and parsing). I'd like to keep the user updated on how long it's going to take, so the Backgroundworker is supposed to display what action its doing and has a progressbar that should fill for every individual action too.
Unfortunately, I can't get it to work, as the progressbar just doesn't update at all and just stays empty.
Here is what I have so far:
private void InitializeBackgroundWorker()
{
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
}
private void buttonParse_Click(object sender, EventArgs e)
{
DescriptionLabel.Visible = true;
progressBar1.Visible = true;
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
Methods.ParsePerfusionData(backgroundWorker1); //Also tried using 'worker' here, but didnt work either
}
And in the method it looks like that:
public static void ParsePerfusionData(BackgroundWorker worker)
{
for (int i = 2; i < Globals.DataList.Count; i++)
{
worker.ReportProgress(i / amount * 100);
rest of the code etc.
}
}
Can I not use a backgroundworker in a global method like that? Thanks in advance!
When i < amount then i / amount * 100 = 0 * 100 = 0.
Simply use i * 100 / amount instead.
Also make sure backgroundWorker1.WorkerReportsProgress = true
You can only report progress between distinct operations. That means either:
using a very modern class that supports this level of reporting. Such a classs might not exist for your case.
reverse engineering parts of the code down to the loop you want to make reporting on. Usually the loop that itterates over files or the like.
GUI updates must be contained to RunWorkerCompelted and ProgressReport events. And depending on how often updates happen, ProgressReport may have to be kept to only updating a progress bar.
Here some old code I wrote with BackgroundWorker wich should get you started:
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion

How can i check if all the files were downloaded?

Before i tried to check if progressBar2 that show overall download progress is at 100% but it's not really working. Is there another way more sure way to check it ?
private void btnDownload_Click(object sender, EventArgs e)
{
//urll.Add("http://download.thinkbroadband.com/1GB.zip");
btnDownload.Enabled = false;
label7.Text = "Downloading...";
getTotalBytes(countryList);
CreateCountryDateTimeDirectories(newList);
downloadFile(newList);
}
private Queue<string> _downloadUrls = new Queue<string>();
private async void downloadFile(IEnumerable<string> urls)
{
foreach (var url in urls)
{
_downloadUrls.Enqueue(url);
}
await DownloadFile();
}
private async Task DownloadFile()
{
if (_downloadUrls.Any())
{
WebClient client = new WebClient();
client.DownloadProgressChanged += ProgressChanged;
client.DownloadFileCompleted += Completed;
var url = _downloadUrls.Dequeue();
sw = Stopwatch.StartNew();
if (url.Contains("true"))
{
await client.DownloadFileTaskAsync(new Uri(url), countriesMainPath + "\\" + currentDownloadCountry + "\\" + count + "Infrared.jpg");
}
else
{
await client.DownloadFileTaskAsync(new Uri(url), countriesMainPath + "\\" + currentDownloadCountry + "\\" + count + "Invisible.jpg");
}
return;
}
}
double percentageTotalDownload = 0;
double totalBytesDownloaded = 0;
private void ProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
// Calculate download speed and output it to labelSpeed.
label3.Text = string.Format("{0} kb/s", (e.BytesReceived / 1024d / sw.Elapsed.TotalSeconds).ToString("0.00"));
// Update the progressbar percentage only when the value is not the same.
double bytesInCurrentDownload = (double)e.BytesReceived;
double totalBytesCurrentDownload = double.Parse(e.TotalBytesToReceive.ToString());
double percentageCurrentDownload = bytesInCurrentDownload / totalBytesCurrentDownload * 100;
ProgressBar1.Value = int.Parse(Math.Truncate(percentageCurrentDownload).ToString());//e.ProgressPercentage;
// Show the percentage on our label.
Label4.Text = e.ProgressPercentage.ToString() + "%";
// Update the label with how much data have been downloaded so far and the total size of the file we are currently downloading
label10.Text = string.Format("{0} MB's / {1} MB's",
(e.BytesReceived / 1024d / 1024d).ToString("0.00"),
(e.TotalBytesToReceive / 1024d / 1024d).ToString("0.00"));
//Let's update ProgressBar2
totalBytesDownloaded = e.BytesReceived + bytesFromCompletedFiles;
percentageTotalDownload = totalBytesDownloaded / totalBytesToDownload * 100;
progressBar2.Value = (int)percentageTotalDownload;
label6.Text = progressBar2.Value.ToString() + "%";
}
long bytesFromCompletedFiles = 0;
// The event that will trigger when the WebClient is completed
private async void Completed(object sender, AsyncCompletedEventArgs e)
{
await DownloadFile();
}
For example if i have 100 urls and it's start downloading then i want to know in the completed event when all the files downloaded and not only one each time.
You can keep track of how many downloads have been completed in an counter variable. Because of the multiple threads that can access that counter, use the Interlocked class to manipulate that counter.
This are the changes needed in your code:
private int urlCount = 0; // keep track of how many urls are processed
private async void downloadFile(IEnumerable<string> urls)
{
urlCount = 0;
foreach (var url in urls)
{
_downloadUrls.Enqueue(url);
urlCount++;
}
// urlCount is now set
await DownloadFile();
}
And here is the handling of the counter and the check if we are done
private async void Completed(object sender, AsyncCompletedEventArgs e)
{
// urlCount will be decremented
// cnt will get its value
var cnt = System.Threading.Interlocked.Decrement(ref urlCount);
if (cnt > 0) {
await DownloadFile();
}
else
{
// call here what ever you want to happen when everything is
// downloaded
"Done".Dump();
}
}
I haven't understood it very well, but what you are trying to accomplish is that your progress bar is showing progress of download all files and not one file then reset then another file that reset.
If that is the case, then why you do not try foreach file get size and sum it to totalBytesToDownload and then use it in progress bar
So
foreach(string url in urll)
{
//get url file size
totalBytesToDownload = totalBytesToDownload + urlSizeYouGot;
}

C# Execute method after background thread finished

I'm using a thread to run a calculation in the background of my program. I start the thread at the start of my program. If I press a button before the thread is finished it will open the statusBar and "openedStatus" is set to true.
This will show the threads current progress and after the thread has finished I would like to execute the last part of my code:
if (openedStatus)
{
sb.Close();
validateBeforeSave();
}
This part of the code will throw an exception though because you can't close the statusbar cross-thread.
Now the question is: How can I execute that last part of the code after the thread is finished?
private StatusBar sb = new StatusBar();
private void startVoorraadCalculationThread()
{
sb.setMaxProgress(data.getProducten().getProductenCopy().Count);
Thread thread = new Thread(new ThreadStart(this.run));
thread.Start();
while (!thread.IsAlive) ;
}
private void run()
{
for (int i = 0; i < data.getProducten().getProductenCopy().Count; i++ )
{
sb.setProgress(i);
sb.setStatus("Calculating Voorraad: " + (i+1) + "/" + data.getProducten().getProductenCopy().Count);
data.getProducten().getProductenCopy()[i].getTotaalVoorraad(data.getMaten());
}
if (openedStatus)
{
sb.Close();
validateBeforeSave();
}
calculationFinished = true;
}
Using a backgroundWorker fixed my problem:
private void startVoorraadCalculationThread()
{
sb.setMaxProgress(data.getProducten().getProductenCopy().Count);
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < data.getProducten().getProductenCopy().Count; i++)
{
sb.setProgress(i);
sb.setStatus("Calculating Voorraad: " + (i + 1) + "/" + data.getProducten().getProductenCopy().Count);
data.getProducten().getProductenCopy()[i].getTotaalVoorraad(data.getMaten());
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (openedStatus)
{
sb.Close();
validateBeforeSave();
}
calculationFinished = true;
}

I'm trying to use backgroundworker but it doesn't work - why?

This is the code of the DoWork event the ProgressChanged event and the RunWorkerCompleted event. The problem is that the progressBar is getting to 100% much faster before the function process operation is end. So when the progressBar is up to 100% I'm getting error exception since the progressBar can't be 101%
I need somehow to make that the progressBar will get progress according to the function process / progress. I guess that something is wrong with my calculation in the DoWork event.
In the Form1 top I added:
Int i;
In the constructor I did:
i = 0;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.RunWorkerAsync();
And this is the events of the Backgroundworker:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
//int currentLength;
//int currentIndex;
//int lastIndex = 0;
string startTag = "T256=\"";
string endTag = "\"";
int startTagWidth = startTag.Length;
//int endTagWidth = endTag.Length;
int index = 0;
h = new StreamReader(#"d:\DeponiaSplit\testingdeponias_Translated.txt");
while ((line = h.ReadLine()) != null)
{
if (index > f.LastIndexOf(startTag))
{
break;
}
int startTagIndex = f.IndexOf(startTag, index);
int stringIndex = startTagIndex + startTagWidth;
index = stringIndex;
int endTagIndex = f.IndexOf(endTag, index);
int stringLength = endTagIndex - stringIndex;
if (stringLength != 0)
{
string test = f.Substring(stringIndex, stringLength);
f = f.Substring(0, stringIndex) + line + f.Substring(stringIndex + stringLength);
if (listBox1.InvokeRequired)
{
textBox1.Invoke(new MethodInvoker(delegate { textBox1.Text = line; }));
}
i = i + 1;
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 1));
}
}
h.Close();
StreamWriter w = new StreamWriter(#"D:\New folder (24)\000004aa.xml");
w.AutoFlush = true;
w.Write(f);
w.Close();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//this.progressBar1.Text = (e.ProgressPercentage.ToString() + "%");
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
this.progressBar1.Text = "Canceled!";
}
else if (!(e.Error == null))
{
this.progressBar1.Text = ("Error: " + e.Error.Message);
}
else
{
this.progressBar1.Text = "Done!";
}
}
Why doesn't it work correctly?
Working using FileStream
Now I wanted to add to the progressBar a label which will show % and the label will move according to the progressBar I don't want to disable the progressBar green color progress just to add % and show the numbers in percentages.
So in the ProgressChanged event I did :
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label3.Text = (e.ProgressPercentage.ToString() + "%");
}
But it doesn't work - the label13 doesn't move only the % changed to 1. I want to see something like 1%...2%...3%... and so on. How can I do it?
Why don't you use File.ReadAllLines, it returns an array of strings. You could report your progress as the percentage of the line you are processing to the total number of lines:
string[] lines = File.ReadAllLines(#"d:\DeponiaSplit\testingdeponias_Translated.txt");
// ...
worker.ReportProgress(i * 100 / lines.Length);
The "ReportProgress()" method is reporting "i" right after "i" has been incremented with the "i+1" statment. i is a variable that is incremented every time you have found a string that has matched your provided parameters. I think the problem here is that you are reporting an integer without context. Do you know how many lines total in the file contain a string that matches your parameters. I would suggest reporting (i/totalNumLinesMatchingParameters) * 100. This would report a number in terms of percentage. However, this does not include the action of writing the File. So you may want to scale the above differently if you intend to include the action of writing the File in your progress bar/progress bar label...

Categories