DEV Community

1001binary
1001binary

Posted on

Download file using HttpClient wrapper asynchronously.

HttpClient is a simple and robust wrapper to send and receive HTTP requests. It's an awesome alternative to the legacy HTTP client .NET api. I like HttpClient the best. It's free, efficient and especially easy to use. You can send HTTP requests and then receive data back only with a couple of code lines.

For instance:
A function to download file with a provided uri and output path.

public static class HttpHelper
{
   private static readonly HttpClient _httpClient = new HttpClient();

   public static async void DownloadFileAsync(string uri
        , string outputPath)
   {
      Uri uriResult;

      if (!Uri.TryCreate(uri, UriKind.Absolute, out uriResult))
         throw new InvalidOperationException("URI is invalid.");

      if (!File.Exists(outputPath))
         throw new FileNotFoundException("File not found."
            , nameof(outputPath));

      byte[] fileBytes = await _httpClient.GetByteArrayAsync(uri);
      File.WriteAllBytes(outputPath, fileBytes);
   }
}
Enter fullscreen mode Exit fullscreen mode

Hope you enjoy this post.

Happy coding :)

Top comments (4)

Collapse
 
alexjitbit profile image
Alex Yumashev

You should NEVER NEVER EVER put HttpClient inside using, you should reuse an existing var and keep it alive thought the lifetime of your app.

HttpClient is intended to be instantiated once and re-used throughout the life of an application. Instantiating an HttpClient class for every request will exhaust the number of sockets available under heavy loads.

docs.microsoft.com/en-us/dotnet/ap...

Collapse
 
1001binary profile image
1001binary

Thanks Alex :). You're right.

The DownloadFile function is just a sample function to demonstrate how to download file using HttpClient asynchronously.

In practice, the existing HttpClient var should be reused throughout the lifetime of the application.

I just updated this post.

Collapse
 
issung profile image
Issun

Can be greatly improved by not loading the entire download into memory with GetByteArrayAsync. Instead open a stream and then send that stream into a file stream.

public static async Task DownloadFileAsync(string uri, string outputPath) 
{
    if (!Uri.TryCreate(uri, UriKind.Absolute, out var uriResult))
        throw new InvalidOperationException("URI is invalid.");

    if (File.Exists(outputPath))
    { 
        throw new InvalidOperationException("File already exists.");
    }

    using (var response = await _httpClient.GetAsync(uriResult, HttpCompletionOption.ResponseHeadersRead))
    {
        response.EnsureSuccessStatusCode();

        using (var fileStream = File.Create(outputPath))
        {
            using (var httpStream = await response.Content.ReadAsStreamAsync())
            {
                await httpStream.CopyToAsync(fileStream);
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
biryazilim profile image
Bir Yazilim Ltd Sti

Nice and Helpfull Code. Regarding your file control existence, case file not exists you may create a new one, or it would be user default option...