HttpClient: Downloading to a Local File

Downloading content to a local file is a common thing to do. The current version of HttpClient doesn’t yet provide out of the box support for saving content to a file but this sample shows how to extend HttpClient with new ways of reading content retrieved using HttpClient. Please see List of ASP.NET Web API and HttpClient Samples for the complete sample solution.

ReadAs Extension Methods

The HttpContent class contains content to be sent to a client (in the case of PUT, POST, etc.) as well as data being read from the server in a response. The basic System.Net.Http NuGet package provides support for reading the content as a stream, a string, or a byte array using one of

  • HttpContent.ReadAsStringAsync
  • HttpContent.ReadAsStreamAsync
  • HttpContent.ReadAsByteArrayAsync

The way to extend how an HttpContent can be consumed is through the ReadAs* extension methods. The System.Net.Formatter NuGet package offers a set of additional ReadAs* methods, for reading and deserializing data at the same time. This sample shows how to add a simple ReadAsFileAsync extension method but the floor is open for any number of ways of reading the content.

LoadIntobufferAsync

In general, when you read data from an HttpContent it is consumed meaning that it can’t be read again (like when you read a non-seekable stream). However, if you want to be able to read the content multiple times then you can use the LoadIntoBufferAsync method to do that. This will cause the content to get read into an internal buffer so that it can consumed multiple times without retrieving it again over the network.

    1: static void Main(string[] args)
    2: {
    3:     HttpClient client = new HttpClient();
    4:  
    5:     // Send asynchronous request
    6:     client.GetAsync(_address).ContinueWith(
    7:         (requestTask) =>
    8:         {
    9:             // Get HTTP response from completed task.
   10:             HttpResponseMessage response = requestTask.Result;
   11:  
   12:             // Check that response was successful or throw exception
   13:             response.EnsureSuccessStatusCode();
   14:  
   15:             // Read content into buffer
   16:             response.Content.LoadIntoBufferAsync();
   17:  
   18:             // The content can now be read multiple times using any ReadAs* extension method
   19:         });
   20:  
   21:     Console.WriteLine("Hit ENTER to exit...");
   22:     Console.ReadLine();
   23: }

ReadAsFileAsync

First we make the ReadAsFileAsync extension method on HttpContent to provide support for reading the content and storing it directly in a local file:

    1: public static class HttpContentExtensions
    2: {
    3:     public static Task ReadAsFileAsync(this HttpContent content, string filename, bool overwrite)
    4:     {
    5:         string pathname = Path.GetFullPath(filename);
    6:         if (!overwrite && File.Exists(filename))
    7:         {
    8:             throw new InvalidOperationException(string.Format("File {0} already exists.", pathname));
    9:         }
   10:  
   11:         FileStream fileStream = null;
   12:         try
   13:         {
   14:             fileStream = new FileStream(pathname, FileMode.Create, FileAccess.Write, FileShare.None);
   15:             return content.CopyToAsync(fileStream).ContinueWith(
   16:                 (copyTask) =>
   17:                 {
   18:                     fileStream.Close();
   19:                 });
   20:         }
   21:         catch
   22:         {
   23:             if (fileStream != null)
   24:             {
   25:                 fileStream.Close();
   26:             }
   27:  
   28:             throw;
   29:         }
   30:     }
   31: }

Downloading a Google Map

Finally we put the two together – in this case we download an image from Google Maps and open it up in the default image viewer (if you don’t have an image viewer then opening the downloaded image will fail but that doesn’t change the download part):

    1: /// <summary>
    2: ///  Downloads a Redmond map from Google Map, saves it as a file and opens the default viewer.
    3: /// </summary>
    4: class Program
    5: {
    6:     static string _address = "https://maps.googleapis.com/maps/api/staticmap?center=Redmond,WA&zoom=14&size=400x400&sensor=false";
    7:  
    8:     static void Main(string[] args)
    9:     {
   10:         HttpClient client = new HttpClient();
   11:  
   12:         // Send asynchronous request
   13:         client.GetAsync(_address).ContinueWith(
   14:             (requestTask) =>
   15:             {
   16:                 // Get HTTP response from completed task.
   17:                 HttpResponseMessage response = requestTask.Result;
   18:  
   19:                 // Check that response was successful or throw exception
   20:                 response.EnsureSuccessStatusCode();
   21:  
   22:                 // Read response asynchronously and save to file
   23:                 response.Content.ReadAsFileAsync("output.png", true).ContinueWith(
   24:                     (readTask) =>
   25:                     {
   26:                         Process process = new Process();
   27:                         process.StartInfo.FileName = "output.png";
   28:                         process.Start();
   29:                     });
   30:             });
   31:  
   32:         Console.WriteLine("Hit ENTER to exit...");
   33:         Console.ReadLine();
   34:     }
   35: }

Have fun!

Henrik

del.icio.us Tags: asp.net,webapi,mvc,rest,httpclient