Start by creating a C# Windows Application project in Visual Studio and on the form drag 3 labels, 2 textboxes, 2 buttons and one progressbar.
As you can see in the form above, the first two labels are just for describing the role of the first two textboxes which are named txtUrl and txtPath. They will obviously hold the URL of the file we want to download and the local path (and filename) to where we want to save it. The default values I set in the project will download a short video from Microsoft’s Channel 9 website to C:\Filename.wmv.
The two buttons – btnDownload and btnStop – are used for starting and stopping a download, while the label below them – lblProgress – is used for showing the current progress of the download in bytes and percentage. Finally, the ProgressBar located at the bottom of the form is entitled prgDownload and it display the progress of the download on a scale from 1 to 100.
Now let’s get into coding. Switch to code view, and the first thing we need to do is to add a few using statements for the namespaces we’re going to use. Add the following 3 lines below the already existing using statements:
1: using System.Net;
2: using System.IO;
3: using System.Threading;
System.Net will be used for connecting to the web server, System.IO will be used for saving the file to the hard drive, and System.Threading will be used for the thread in which we’re going to put the downloading process.
Inside the form’s class, right above the constructor, add the following lines:
1: // The thread inside which the download happens
2: private Thread thrDownload;
3: // The stream of data retrieved from the web server
4: private Stream strResponse;
5: // The stream of data that we write to the harddrive
6: private Stream strLocal;
7: // The request to the web server for file information
8: private HttpWebRequest webRequest;
9: // The response from the web server containing information about the file
10: private HttpWebResponse webResponse;
11: // The progress of the download i ain percentage
12: private static int PercentProgress;
13: // The delegate which we will call from the thread to update the form
14: private delegate void UpdateProgessCallback(Int64 BytesRead, Int64 TotalBytes);
As you can see, we are creating a few objects that we’re going to use later in the code. The object that stands out the most is not really an object, but a delegate: UpdateProgressCallback – this delegate will be used to call a method you’ll see later in our code – UpdateProgress – from inside the thread. Because you see, from inside a thread we can’t update the form elements directly (the label and the progress bar), so we need to create a delegate first, that takes the same arguments as the method. In our case the arguments are two Int64 variables that hold the number of bytes we downloaded from the server by now, and the number of total bytes the file has. Int64 is needed because this number can be really big.
Now let’s review the biggest piece of the code. The one where the file actually gets downloaded. We’ll put this code in a method called Download() which we’re going to call in a thread when the download button is pressed. Here goes the method, below it there’s more explanation of the code:
1: private void Download()
2: {
3: using (WebClient wcDownload = new WebClient())
4: {
5: try
6: {
7: // Create a request to the file we are downloading
8: webRequest = (HttpWebRequest)WebRequest.Create(txtUrl.Text);
9: // Set default authentication for retrieving the file
10: webRequest.Credentials = CredentialCache.DefaultCredentials;
11: // Retrieve the response from the server
12: webResponse = (HttpWebResponse)webRequest.GetResponse();
13: // Ask the server for the file size and store it
14: Int64 fileSize = webResponse.ContentLength;
15:
16: // Open the URL for download
17: strResponse = wcDownload.OpenRead(txtUrl.Text);
18: // Create a new file stream where we will be saving the data (local drive)
19: strLocal = new FileStream(txtPath.Text, FileMode.Create, FileAccess.Write, FileShare.None);
20:
21: // It will store the current number of bytes we retrieved from the server
22: int bytesSize = 0;
23: // A buffer for storing and writing the data retrieved from the server
24: byte[] downBuffer = new byte[2048];
25:
26: // Loop through the buffer until the buffer is empty
27: while ((bytesSize = strResponse.Read(downBuffer, 0, downBuffer.Length)) > 0)
28: {
29: // Write the data from the buffer to the local hard drive
30: strLocal.Write(downBuffer, 0, bytesSize);
31: // Invoke the method that updates the form's label and progress bar
32: this.Invoke(new UpdateProgessCallback(this.UpdateProgress), new object[] { strLocal.Length, fileSize });
33: }
34: }
35: finally
36: {
37: // When the above code has ended, close the streams
38: strResponse.Close();
39: strLocal.Close();
40: }
41: }
42: }
43:
The first line inside the method mentions that inside this method we’ll be using the wcDownload object, which can be disposed after we’re finished. If any error happens within the code, we have a finally block which closes the streams to prevent keeping a connection opened uselessly and to prevent the local file from being locked by the code.
Inside the try block we first retrieve information about the file using HttpWebRequest and HttpWebResponse objects. Note that some servers don’t give information about the size of the file, case in which we can only download blindly. If the web server did not return any information regarding the size of the file, webResponse.ContentLength will return -1.
After we get the size of the file, we define the stream that retrieves the bytes from the server, and the stream that saves the file to the hard drive. Before starting to stream the bytes down the cables, we create a buffer where we store the data that is written to the hard drive file. The buffer is 2048 bytes in size, but you can change it to a different value if you prefer.
In the while loop we loop through the buffer and write the content of the buffer to the file on the local drive. We also use the Invoke method of the form to call UpdateProgressCallback (the delegate of UpdateProgress). In the array we pass two parameters that UpdateProgress accepts: how much we downloaded until now (by measuring the length of the local stream), and how big the total file is. If you don’t have any knowledge of threading in C#, you probably would have guessed that you can update the form elements (labels, progress bars, etc.) directly, but for good enough reasons you can’t.
If we were to call the Download() method directly, then we wouldn’t have to use this.Invoke to call the UpdateProgress method. Speaking of UpdateProgress, let’s see how this method looks like:
1: private void UpdateProgress(Int64 BytesRead, Int64 TotalBytes)
2: {
3: // Calculate the download progress in percentages
4: PercentProgress = Convert.ToInt32((BytesRead * 100) / TotalBytes);
5: // Make progress on the progress bar
6: prgDownload.Value = PercentProgress;
7: // Display the current progress on the form
8: lblProgress.Text = "Downloaded " + BytesRead + " out of " + TotalBytes + " (" + PercentProgress + "%)";
9: }
We do a simple math calculation to get the percentage (0 to 100) and we set it on the ProgressBar to reflect the progress. We also set the label with information on the progress of the download.
We’re done with the methods for this application, now we only need to create the two event handlers for the Download and Stop buttons. Double clicking btnDownload in Visual Studio 2005 will create the Click event handler for you. Use the following code:
1: private void btnDownload_Click(object sender, EventArgs e)
2: {
3: // Let the user know we are connecting to the server
4: lblProgress.Text = "Download Starting";
5: // Create a new thread that calls the Download() method
6: thrDownload = new Thread(Download);
7: // Start the thread, and thus call Download()
8: thrDownload.Start();
9: }
In the code above we start a new thread, to which we pass the name of the method (without the parenthesis). Then we start the thread. The reason we need to use a thread and we can’t just call the method from inside the Click event is because in that case our application would completely hang while downloading the file. It would become unusable and unresponsive, as if it crashed.
Finally, we have the code for the stop button:
1: private void btnStop_Click(object sender, EventArgs e)
2: {
3: // Close the web response and the streams
4: webResponse.Close();
5: strResponse.Close();
6: strLocal.Close();
7: // Abort the thread that's downloading
8: thrDownload.Abort();
9: // Set the progress bar back to 0 and the label
10: prgDownload.Value = 0;
11: lblProgress.Text = "Download Stopped";
12: }
To make this a real download manager, we’d have to add resume options, and a download list so that we give the user the option to download multiple files at once, or schedule them. This will be covered in a future tutorial.
Below is the entire source code of Form1.csthat you can also view in the Visual Studio 2005 project files attached to this tutorial.
1: using System;
2: using System.Collections.Generic;
3: using System.ComponentModel;
4: using System.Data;
5: using System.Drawing;
6: using System.Text;
7: using System.Windows.Forms;
8: using System.Net;
9: using System.IO;
10: using System.Threading;
11:
12: namespace DownloadManager
13: {
14: public partial class Form1 : Form
15: {
16: // The thread inside which the download happens
17: private Thread thrDownload;
18: // The stream of data retrieved from the web server
19: private Stream strResponse;
20: // The stream of data that we write to the harddrive
21: private Stream strLocal;
22: // The request to the web server for file information
23: private HttpWebRequest webRequest;
24: // The response from the web server containing information about the file
25: private HttpWebResponse webResponse;
26: // The progress of the download in percentage
27: private static int PercentProgress;
28: // The delegate which we will call from the thread to update the form
29: private delegate void UpdateProgessCallback(Int64 BytesRead, Int64 TotalBytes);
30:
31: public Form1()
32: {
33: InitializeComponent();
34: }
35:
36: private void btnDownload_Click(object sender, EventArgs e)
37: {
38: // Let the user know we are connecting to the server
39: lblProgress.Text = "Download Starting";
40: // Create a new thread that calls the Download() method
41: thrDownload = new Thread(Download);
42: // Start the thread, and thus call Download()
43: thrDownload.Start();
44: }
45:
46: private void UpdateProgress(Int64 BytesRead, Int64 TotalBytes)
47: {
48: // Calculate the download progress in percentages
49: PercentProgress = Convert.ToInt32((BytesRead * 100) / TotalBytes);
50: // Make progress on the progress bar
51: prgDownload.Value = PercentProgress;
52: // Display the current progress on the form
53: lblProgress.Text = "Downloaded " + BytesRead + " out of " + TotalBytes + " (" + PercentProgress + "%)";
54: }
55:
56: private void Download()
57: {
58: using (WebClient wcDownload = new WebClient())
59: {
60: try
61: {
62: // Create a request to the file we are downloading
63: webRequest = (HttpWebRequest)WebRequest.Create(txtUrl.Text);
64: // Set default authentication for retrieving the file
65: webRequest.Credentials = CredentialCache.DefaultCredentials;
66: // Retrieve the response from the server
67: webResponse = (HttpWebResponse)webRequest.GetResponse();
68: // Ask the server for the file size and store it
69: Int64 fileSize = webResponse.ContentLength;
70:
71: // Open the URL for download
72: strResponse = wcDownload.OpenRead(txtUrl.Text);
73: // Create a new file stream where we will be saving the data (local drive)
74: strLocal = new FileStream(txtPath.Text, FileMode.Create, FileAccess.Write, FileShare.None);
75:
76: // It will store the current number of bytes we retrieved from the server
77: int bytesSize = 0;
78: // A buffer for storing and writing the data retrieved from the server
79: byte[] downBuffer = new byte[2048];
80:
81: // Loop through the buffer until the buffer is empty
82: while ((bytesSize = strResponse.Read(downBuffer, 0, downBuffer.Length)) > 0)
83: {
84: // Write the data from the buffer to the local hard drive
85: strLocal.Write(downBuffer, 0, bytesSize);
86: // Invoke the method that updates the form's label and progress bar
87: this.Invoke(new UpdateProgessCallback(this.UpdateProgress), new object[] { strLocal.Length, fileSize });
88: }
89: }
90: finally
91: {
92: // When the above code has ended, close the streams
93: strResponse.Close();
94: strLocal.Close();
95: }
96: }
97: }
98:
99: private void btnStop_Click(object sender, EventArgs e)
100: {
101: // Close the web response and the streams
102: webResponse.Close();
103: strResponse.Close();
104: strLocal.Close();
105: // Abort the thread that's downloading
106: thrDownload.Abort();
107: // Set the progress bar back to 0 and the label
108: prgDownload.Value = 0;
109: lblProgress.Text = "Download Stopped";
110: }
111: }
112: }
113:
114:
115:
Please Do comment ,if you liked my tutorial.