ASP.NET Web API file download service with resume support


ASP.NET Web API provides out of the box support for streaming binary files to the client. However for more advanced scenarios you need to add custom logic to handle pause/resume functionality by handling appropriate HTTP Headers. In this post I will try to address this problem and build a resume-supporting file download service using two different approaches:

  • stream wrapper for FileStream that can return partial data,
  • memory mapped files.
Memory mapped files seem to be an interesting candidate as they may offer performance benefits such as memory caching and optimized file access managed by virtual memory manager.


Simple file download service

Thanks to StreamContent class, creating basic file download service in ASP.NET Web API is a relatively straightforward task. Let’s start by implementing a basic scenario, where files are served from a directory.
Instead of dealing with file system access directly in controllers I usually like to encapsulate this functionality in a dedicated object, which makes unit testing/mocking easier and makes code tidier. For our examples we will create IFileProvider interface that exposes three operations:

The actual implementation will use app settings in web.config file to configure storage folder location.


With file access logic ready we can write code that actually serves the data. A simple Web API controller that streams files will look like this:

It is a basic version, yet it seems to work fine. If you wanted to use it in more advanced scenarios however, there are a couple of potential problems to face.
First of all when the transfer is interrupted for whatever reason, the client has to start downloading from the beginning. This is unacceptable when serving large files and would be a major annoyance for people using mobile connections that drop often. Another problem is that the implementation above is not very client friendly in terms of http support (eg. HEAD verb).

Adding resume support

There are two main areas that we need to add more logic to in order to introduce pause/resume functionality:

  • extend HTTP protocol support – most importantly by handling Range header properly,
  • use a Stream that is capable of  returning file portion (from byte A to byte B).
Why should we implement HEAD verb in the controller? Let’s imagine we were to write software that downloads large files over HTTP using our service. Ideally we would like to have a mechanism that could tell us how big is the file (by returning Content-Length header) and whether or not the service can serve us partial data (by returning Accept-Ranges header) without actually getting the data. This is exactly what HEAD does as it is designed to be identical to GET except that the server must not return the body (headers only).
Accept-Ranges is returned by the server in order to indicate that it can return bytes ranges of a requested resource. Moreover if partial content has been returned, the server should return 206 Partial Content status code along with Content-Range header that contains ranges returned. If the client requests range that is out of bounds for a given resource 416 Requested Range Not Satisfiable status should be returned.
Here is an example added for clarity.

This is a helper class used to store some information passed in HTTP headers.

The controller itself can look like this:

Another important part of this solution is a stream implementation that can return byte range from a file. This is actually a wrapper for FileStream, please note that this code is largely untested, although gives an idea about approach – you have been warned ;)

If  you think about this performance-wise, its not the most optimal approach as every time a file is being requested we need  to read it from the disk and disks are very slow (compared to RAM) and disk access may become bottleneck very fast. It becomes evident that for more advanced scenarios some kind of a caching mechanism would be a good optimization.

Using memory-mapped files

Memory mapped file is a portion of virtual memory that has been mapped to a file. This is not a new concept and has been around in Windows (and other OSes) for many years, but just recently (from NET 4 that is) has been made available to C# programmers as a managed API. Memory mapped files allow processes to modify and read files as if they were reading and writing to the memory. If my memory serves me well IPC in Windows is actually implemented using this feature.

Memory mapped files

Please note that the files are mapped and not copied into virtual memory, but from program’s perspective its transparent as Windows loads parts of physical files as they are accessed by application. Another advantage of MMF is that the system performs transfers in 4K chunks of data (pages) and virtual-memory manager (VMM) decides when it should free those pages up. Windows is highly optimized for page-related IO operations, and it tries to minimize the number of times the hard disk head has to move. In other words by using MMF you have a guarantee that the OS will optimize disk access and additionally you get a form of memory cache.

Because files are mapped to virtual memory, to serve big files we need to run our application in 64 bit mode, otherwise it wouldn’t be able to address all space needed.For this example, make sure to change target platform to x64 in project properties.

I’ve removed code that is identical to FilesController. Please note that we have 1-1 relationship between a file (or its name to be more precise) and a map name. This means we use same map for all requests asking for the same filename.

Both controllers should provide pause/resume function.

Hope you find this post useful, complete source code is available as usually on bitbucket. Enjoy!

Written by Piotr Walat

October 18th, 2012 at 8:00 am

  • Jeremychild

    This is great!

  • http://ghadzhigeorgiev.wordpress.com Georgi Hadzhigeorgiev


  • http://www.sholo.net/ scottt732

    Perfect timing.  I need to setup server-to-server file transfer over HTTP this week and this will be enormously useful.  Thanks.

  • http://twitter.com/niallcollins Niall Collins

    Explain to me the benefit of doing this rather than referencing a file directly on the file system? Btw: interesting article.

    • http://www.piotrwalat.net/ Piotr Walat

      By default you should always try to use static file serving (that is handled without ASP.NET but by IIS/other web server itself) – so I think that is what you are suggesting.
      However sometimes you may need to:

      - serve the files dynamically depending on user input (eg. http://ninite.com/ like service),

      - support advanced/custom authorization and authentication scenarios,

      - self-host Web API in windows service or in your desktop  app, shipping the service with your product (for example when deploying customer apps that provide simple fileserving functionality).

      • http://twitter.com/niallcollins Niall Collins

        Ah, I see. Thanks for the explanation.

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1214

  • Hari

    Do you have a web client to test?? Sorry m a bit lazy :-) 

  • kp

    SO Useful

  • thani

    Very nice article. This was a pain before .But you showed us how MMF will resolve those problems. hats off

  • Douglas Kirschman

    Nice post.  Well explained too.  I’ll try it out and post the results.  Thanks.