Creating a download manager in C#

Learn how to create a download manager with a progress indicator in C# / .NET 2.0 using stream readers, web clients, web requests, buffers and more. You will also be learning the basics of threading in C#.

Awards:

Recommended by Geekpedia5 stars rated
Recommended: Recommended by the Geekpedia staff.
5 stars: Programming tutorial/source code has a rate higher than 4.5.

Sometime in 2004 on Geekpedia we saw a tutorial entitled Using WebClient to download a file, where we would use a few lines of code to save a file retrieved from the web through http on our hard drive. Now we’re taking one step forward, to complicate the things just a little, so that we can display the progress of the download.
For displaying the progress we’re going to use a ProgressBar and a Label. For calculating it, we’ll need to retrieve the total size of the file from the web server, and calculate what we’ve downloaded by now.

This tutorial teaches you how to create a download manager in C# 2.0 (.NET Framework 2.0), using Visual Studio 2005. However, you can always convert this code to Visual Basic .NET, or to an earlier version of .NET Framework such as .NET 1.1.

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:

using System.Net;
using System.IO;
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:

// The thread inside which the download happens
private Thread thrDownload;
// The stream of data retrieved from the web server
private Stream strResponse;
// The stream of data that we write to the harddrive
private Stream strLocal;
// The request to the web server for file information
private HttpWebRequest webRequest;
// The response from the web server containing information about the file
private HttpWebResponse webResponse;
// The progress of the download in percentage
private static int PercentProgress;
// The delegate which we will call from the thread to update the form
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:

private void Download()
{
   using (WebClient wcDownload = new WebClient())
   {
      try

      {
         // Create a request to the file we are downloading
         webRequest = (HttpWebRequest)WebRequest.Create(txtUrl.Text);
         // Set default authentication for retrieving the file
         webRequest.Credentials = CredentialCache.DefaultCredentials;
         // Retrieve the response from the server
         webResponse = (HttpWebResponse)webRequest.GetResponse();
         // Ask the server for the file size and store it
         Int64 fileSize = webResponse.ContentLength;

         // Open the URL for download
         strResponse = wcDownload.OpenRead(txtUrl.Text);
         // Create a new file stream where we will be saving the data (local drive)
         strLocal = new FileStream(txtPath.Text, FileMode.Create, FileAccess.Write, FileShare.None);

         // It will store the current number of bytes we retrieved from the server
         int bytesSize = 0;
         // A buffer for storing and writing the data retrieved from the server
         byte[] downBuffer = new byte[2048];

         // Loop through the buffer until the buffer is empty
         while ((bytesSize = strResponse.Read(downBuffer, 0, downBuffer.Length)) > 0)
         {
            // Write the data from the buffer to the local hard drive
            strLocal.Write(downBuffer, 0, bytesSize);
            // Invoke the method that updates the form's label and progress bar
            this.Invoke(new UpdateProgessCallback(this.UpdateProgress), new object[] { strLocal.Length, fileSize });
         }
      }
      finally
      {
         // When the above code has ended, close the streams
         strResponse.Close();
         strLocal.Close();
      }
   }
}

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:

private void UpdateProgress(Int64 BytesRead, Int64 TotalBytes)
{
   // Calculate the download progress in percentages
   PercentProgress = Convert.ToInt32((BytesRead * 100) / TotalBytes);
   // Make progress on the progress bar
   prgDownload.Value = PercentProgress;
   // Display the current progress on the form
   lblProgress.Text = "Downloaded " + BytesRead + " out of " + TotalBytes + " (" + PercentProgress + "%)";
}

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:

private void btnDownload_Click(object sender, EventArgs e)
{
   // Let the user know we are connecting to the server
   lblProgress.Text = "Download Starting";
   // Create a new thread that calls the Download() method
   thrDownload = new Thread(Download);
   // Start the thread, and thus call Download()
   thrDownload.Start();
}

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:

private void btnStop_Click(object sender, EventArgs e)
{
   // Close the web response and the streams
   webResponse.Close();
   strResponse.Close();
   strLocal.Close();
   // Abort the thread that's downloading
   thrDownload.Abort();
   // Set the progress bar back to 0 and the label
   prgDownload.Value = 0;
   lblProgress.Text = "Download Stopped";
}

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.cs that you can also view in the Visual Studio 2005 project files attached to this tutorial.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.IO;
using System.Threading;

namespace DownloadManager
{
   public partial class Form1 : Form
   {
      // The thread inside which the download happens
      private Thread thrDownload;
      // The stream of data retrieved from the web server
      private Stream strResponse;
      // The stream of data that we write to the harddrive
      private Stream strLocal;
      // The request to the web server for file information
      private HttpWebRequest webRequest;
      // The response from the web server containing information about the file
      private HttpWebResponse webResponse;
      // The progress of the download in percentage
      private static int PercentProgress;
      // The delegate which we will call from the thread to update the form
      private delegate void UpdateProgessCallback(Int64 BytesRead, Int64 TotalBytes);

      public Form1()
      {
         InitializeComponent();
      }

      private void btnDownload_Click(object sender, EventArgs e)
      {
         // Let the user know we are connecting to the server
         lblProgress.Text = "Download Starting";
         // Create a new thread that calls the Download() method
         thrDownload = new Thread(Download);
         // Start the thread, and thus call Download()
         thrDownload.Start();
      }

      private void UpdateProgress(Int64 BytesRead, Int64 TotalBytes)
      {
         // Calculate the download progress in percentages
         PercentProgress = Convert.ToInt32((BytesRead * 100) / TotalBytes);
         // Make progress on the progress bar
         prgDownload.Value = PercentProgress;
         // Display the current progress on the form
         lblProgress.Text = "Downloaded " + BytesRead + " out of " + TotalBytes + " (" + PercentProgress + "%)";
      }

      private void Download()
      {
         using (WebClient wcDownload = new WebClient())
         {
            try
            {
               // Create a request to the file we are downloading
               webRequest = (HttpWebRequest)WebRequest.Create(txtUrl.Text);
               // Set default authentication for retrieving the file
               webRequest.Credentials = CredentialCache.DefaultCredentials;
               // Retrieve the response from the server
               webResponse = (HttpWebResponse)webRequest.GetResponse();
               // Ask the server for the file size and store it
               Int64 fileSize = webResponse.ContentLength;

               // Open the URL for download
               strResponse = wcDownload.OpenRead(txtUrl.Text);
               // Create a new file stream where we will be saving the data (local drive)
               strLocal = new FileStream(txtPath.Text, FileMode.Create, FileAccess.Write, FileShare.None);

               // It will store the current number of bytes we retrieved from the server
               int bytesSize = 0;
               // A buffer for storing and writing the data retrieved from the server
               byte[] downBuffer = new byte[2048];

               // Loop through the buffer until the buffer is empty
               while ((bytesSize = strResponse.Read(downBuffer, 0, downBuffer.Length)) > 0)
               {
                  // Write the data from the buffer to the local hard drive
                  strLocal.Write(downBuffer, 0, bytesSize);
                  // Invoke the method that updates the form's label and progress bar
                  this.Invoke(new UpdateProgessCallback(this.UpdateProgress), new object[] { strLocal.Length, fileSize });
               }
            }
            finally
            {
               // When the above code has ended, close the streams
               strResponse.Close();
               strLocal.Close();
            }
         }
      }

      private void btnStop_Click(object sender, EventArgs e)
      {
         // Close the web response and the streams
         webResponse.Close();
         strResponse.Close();
         strLocal.Close();
         // Abort the thread that's downloading
         thrDownload.Abort();
         // Set the progress bar back to 0 and the label
         prgDownload.Value = 0;
         lblProgress.Text = "Download Stopped";
      }
   }
}

You can now proceed to the second part of this tutorial, entitled Creating an advanced download manager in C#.

Nathan Pakovskie is an esteemed senior developer and educator in the tech community, best known for his contributions to Geekpedia.com. With a passion for coding and a knack for simplifying complex tech concepts, Nathan has authored several popular tutorials on C# programming, ranging from basic operations to advanced coding techniques. His articles, often characterized by clarity and precision, serve as invaluable resources for both novice and experienced programmers. Beyond his technical expertise, Nathan is an advocate for continuous learning and enjoys exploring emerging technologies in AI and software development. When he’s not coding or writing, Nathan engages in mentoring upcoming developers, emphasizing the importance of both technical skills and creative problem-solving in the ever-evolving world of technology. Specialties: C# Programming, Technical Writing, Software Development, AI Technologies, Educational Outreach

Leave a Reply

Your email address will not be published. Required fields are marked *

Back To Top