How to download a webfile with C# and show download progress synchronously and asynchronously


There are several types of files you can download from the web : documents, pictures, videos, extensions etc. Whatever your reason is (an update feature in your application, get extra resources etc.), know how to download a file with C# is a must nowadays.

To achieve our task, we are going to depend of the WebClient Class of .NET. The WebClient is a higher-level abstraction built on top of HttpWebRequest to simplify the most common tasks.

Before continue, don't forget to add the required use statement to be able to download files using the WebClient in the top of your class:

using System.Net;

Syncronously

The most easy way to download a file syncronously (will freeze the UI), thanks to the WebClient class is going to be of 5 lines :

// A web URL with a file response
string myWebUrlFile = "https://www.google.com/images/icons/ui/doodle_plus/logo.png";
// Local path where the file will be saved
string myLocalFilePath = "C:/users/desktop/logo.png";

using (var client = new WebClient())
{
    client.DownloadFile(myWebUrlFile, myLocalFilePath);
}

With the previous example you should understand how the DownloadFile method works. However, depends of you how are going to implement and refine the method.

Note that in our example we use the using statement as the WebClient implements IDisposable as a good practice.

The following snippet will download a file in the desktop with its original name (which is retrieven from the url with the getFilename method):

/// <summary>
/// Download a file in the desktop path and save it with the original filename.
/// </summary>
private void downloadFile()
{
    string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
    // Change the url by the value you want (a textbox or something else)
    string url = "https://www.google.com/images/icons/ui/doodle_plus/logo.png";
    // Get filename from URL
    string filename = getFilename(url);

    using (var client = new WebClient())
    {
        client.DownloadFile(url, desktopPath + "/" + filename);
    }

    MessageBox.Show("Download ready");
}

/// <summary>
/// Get the filename from a web url : 
/// 
/// www.google.com/image.png -> returns : image.png
/// 
/// </summary>
/// <param name="hreflink"></param>
/// <returns></returns>
private string getFilename(string hreflink)
{
    Uri uri = new Uri(hreflink);
  
    string filename = System.IO.Path.GetFileName(uri.LocalPath);

    return filename; 
}

To test the snippet, just execute the downloadFile method with some action i.e a button click.

Asynchronously

Usually, normally and obviously we don't want to freeze the UI and you should prefer always the asynchronous way instead (unless you have an exception in your case).

In this case we are going to use the WebClient.DownloadFileAsync method 

/// <summary>
/// Download a file asynchronously in the desktop path, show the download progress and save it with the original filename.
/// </summary>
private void downloadFile()
{
    string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
    // This will download a large image from the web, you can change the value
    // i.e a textbox : textBox1.Text
    string url ="http://feelgrafix.com/data/wallpaper-hd/Wallpaper-HD-11.jpg"
    string filename = getFilename(url);

    using (WebClient wc = new WebClient())
    {
        wc.DownloadProgressChanged += wc_DownloadProgressChanged;
        wc.DownloadFileCompleted += wc_DownloadFileCompleted;
        wc.DownloadFileAsync(new Uri(url), desktopPath + "/" + filename);
    }
}

/// <summary>
///  Show the progress of the download in a progressbar
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    // In case you don't have a progressBar Log the value instead 
    // Console.WriteLine(e.ProgressPercentage);
    progressBar1.Value = e.ProgressPercentage;
}

private void wc_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
    progressBar1.Value = 0;

    if (e.Cancelled)
    {
        MessageBox.Show("The download has been cancelled");
        return;
    }

    if (e.Error != null) // We have an error! Retry a few times, then abort.
    {
        MessageBox.Show("An error ocurred while trying to download file");
        
        return;
    }

    MessageBox.Show("File succesfully downloaded");        
}

As the method is asynchronous, we need to instance the callbacks properly in the downloadFile method.

To test the snippet, add a progressbar to your form and execute the downloadFile method with some action i.e a button click.

As a plus, you can show the total of pending bytes from the filesize (in bytes) of the file in the DownloadProgressChanged event :

private void wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    Console.WriteLine(e.ProgressPercentage + "% | " + e.BytesReceived + " bytes out of " + e.TotalBytesToReceive + " bytes retrieven.");
    // 50% | 5000 bytes out of 10000 bytes retrieven.
}

Cancelling an Asynchronous download

The possibility to cancel a download is an important basic in the lifecycle of a file download.

To cancel a WebClient download you just need to use the CancelAsync method of the created web client.

WebClient client = new WebClient();

private void downloadFile()
{
   string url = "https://www.google.com/images/icons/ui/doodle_plus/logo.png";
   string myLocalFilePath = "C:/users/desktop/logo.png";

   using (client)
   {
       client.DownloadFileAsync(new Uri(url), myLocalFilePath);
   }
}

private void cancelDownload()
{
   client.CancelAsync();
}

Note: as it's easier to append listeners in the class instead inside the downloadFile method, we expose the client in the global scope to be accesible from the cancelDownload and downloadFile methods. To test the snippet, just add the methods as action of a pair of buttons.

Remember, to check if a file download has been cancelled add the DownloadFileCompleted callback and verify the event.Cancelled value as shown in the asynchronous example.

Conclusion

Independently from the protocol (http or https) the file will be downloaded without any kind of issue (as long as there is internet) if it's allowed and accessible from the server.

You can use a little trick to prevent the creation of empty files in case there's no internet available using the GetIsNetworkAvailable from the NetworkInterface :

if (System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
{
    MessageBox.Show("Internet available, proceed with the download");
}

Have fun

Become a more social person