How to download files using C#

Downloading files programmatically is a common task that most programming languages expose different APIs for.

I believe it is useful to have examples to refer to for how to accomplish this in your language of choice, both synchronously and asynchronously.

This article covers how to download files with C# using the classes and methods that are conveniently built into the .NET Framework.

The options

When using C# there are two main options that .NET provides us with.

These are the WebClient and HttpClient classes which I will cover in the following sections.

Note that you can also work with the HttpRequestMessage class directly for low-level access to HTTP requests, but I find that this approach is rarely required, especially not for simpler operations like downloading files.


Since the very first versions of the .NET Framework, the WebClient class has provided some really useful methods for working with files.

This includes methods to download files, strings, and arbitrary binary data using byte arrays. The WebClient class also includes methods for uploading resources to web servers.

Let’s look at a few examples of how to download a file using WebClient.

Synchronous example

First of all, make sure you have the appropriate using statement in place, as follows.

using System.Net;

This is needed in order to use the WebClient class without requiring a fully qualified namespace.

The WebClient class itself is really easy to use. The very simplest example of downloading a file is as follows.

using (var client = new WebClient())
    client.DownloadFile("", "300.png");

The DownloadFile method accepts a URL to download a file from and a local file path to download the file to.

WebClient implements the IDisposable interface, so it is best practice to wrap the code in a using block to make sure resources are properly disposed of after we’ve finished.

All of the above happens synchronously i.e. the code waits until the file download has completed before continuing program execution past the DownloadFile method call.

Asynchronous example

What if we want to download a file asynchronously and report progress?

Unfortunately, the DownloadFile method does not provide a way of reporting progress, as it is a synchronous method.

However, there is a built-in way of asynchronously downloading a file and reporting progress with the WebClient class, as demonstrated in the example below.

using (var client = new WebClient())
    client.DownloadFileCompleted   += (s, e) => Console.WriteLine("Download file completed.");
    client.DownloadProgressChanged += (s, e) => Console.WriteLine($"Downloading {e.ProgressPercentage}%");
    client.DownloadFileAsync(new Uri(""), "300.png");

The above code is very similar to the synchronous example. However, we’re now calling the DownloadFileAsync method which expects a Uri (Uniform Resource Identifier) as the first parameter.

Additionally, two event handlers are wired up before the file download commences.

The DownloadProgressChanged event is fired periodically as the download progresses and provides access to some useful properties such as BytesReceived and ProgressPercentage.

As expected, the DownloadFileCompleted event is fired whenever the file download has completed.

This is all “well and good”, but what if we want to get progress updates and wait for the file download to complete before allowing the code to continue to the next statement?

As it stands, the above code will continue past the DownloadFileAsync method call while the download is in progress, since it is an asynchronous method call.

Asynchronous wait example

In order to download the file asynchronously using the DownloadFileAsync method and wait until the download has completed before continuing program execution, we need to dip into the world of reset events.

The example below demonstrates how to accomplish this.

using (var client          = new WebClient())
using (var completedSignal = new AutoResetEvent(false))
    client.DownloadFileCompleted += (s, e) =>
        Console.WriteLine("Download file completed.");
    client.DownloadProgressChanged += (s, e) => Console.WriteLine($"Downloading {e.ProgressPercentage}%");
    client.DownloadFileAsync(new Uri(""), "300.png");

The AutoResetEvent in the above code allows us to wait at the point where the WaitOne method is called.

When the DownloadFileCompleted event is fired, the Set method on the AutoResetEvent sends a signal that allows the code to proceed to the next statement.

Note that the use of AutoResetEvent as shown above, is a neat little trick that can be applied to any other asynchronous methods you want to call and subsequently wait for completion.

Async await example

Although the above example works, there is a cleaner way to achieve the same result, providing you are targeting .NET Framework 4.5 or greater, or .NET Core 2.0 (.NET Standard 2.0) or greater.

The example below demonstrates this approach.

using (var client = new WebClient())
    client.DownloadFileCompleted   += (s, e) => Console.WriteLine("Download file completed.");
    client.DownloadProgressChanged += (s, e) => Console.WriteLine($"Downloading {e.ProgressPercentage}%");
    await client.DownloadFileTaskAsync("", "300.png");

By using the await keyword when calling the DownloadFileTaskAsync method, the code will wait until the file download has completed, while at the same time carrying out the download asynchronously and firing the progress events.

This is a much nicer approach if you’re able to use it as a result of targeting a new enough version of .NET and you’re in an async context.

Note that whatever method you use the above code within needs to be marked with the async keyword.


Since .NET Framework 4.5, the HttpClient class has quickly taken over as the recommended means of working with any kind of HTTP request, including file downloads.

HttpClient offers lots of different methods and is very powerful.

Before attempting to use the HttpClient class, make sure you have the appropriate using statement in place, as follows.

using System.Net.Http;

Here’s the simplest possible example of downloading a file using HttpClient.

var httpClient = new HttpClient();
using (var stream = await httpClient.GetStreamAsync(""))
    using (var fileStream = new FileStream("300.png", FileMode.CreateNew))
        await stream.CopyToAsync(fileStream);

Note that even though HttpClient implements IDisposable it is recommended that you create one instance of HttpClient and reuse this throughout your program to avoid problems such as socket exhaustion. I’ve simply newed up a HttpClient instance inline in the above example for demonstration purposes.

As you can see from the above example, the code required to download a file using HttpClient is a little lower level compared to using WebClient, since we need to work with streams directly. Nonetheless, the code is still straightforward to follow along with.

Note that as per the previous example, whatever method you use the above code within needs to be marked with the async keyword.

For any new development work, it is recommended that you use HttpClient. It has a number of advantages over WebClient, including more configuration options and it facilitates easier mocking and testing.

However, it does have some disadvantages, such as the lack of built-in progress reporting. If this is a requirement and you want to use HttpClient you’ll need to roll your own solution for this.


In this article, I have covered the two main ways of downloading files using C# and the .NET Framework.

WebClient makes it really easy to download files, with its high-level API and it is available regardless of what .NET version you are targeting.

Although it is technically ‘obsolete’ WebClient can be a good choice for simple scenarios.

HttpClient is now the recommended way to work with HTTP requests, including file downloads, from .NET Framework 4.5 onwards.

Use HttpClient whenever you need more control and as the recommended option for new development.

I hope you enjoyed this post! Comments are always welcome and I respond to all questions.

If you like my content and it helped you out, please check out the button below 🙂