Browse DevX
Sign up for e-mail newsletters from DevX


Tracking and Resuming Large File Downloads in ASP.NET : Page 2

It's notoriously difficult to deal with large file downloads in Web applications, so for most sites, woe betide users if their download gets interrupted. But it doesn't have to be that way; you can make your ASP.NET applications capable of serving resumable large-file downloads. While you're at it, you can track the download progress so you can handle dynamically created files—and you can get all this without old-fashioned ISAPI DLLs and without unmanaged C++ code.

HTTP Protocol Header Support
It turns out that the HTTP protocol supports headers designed for use with interrupted downloads. Using a handful of HTTP headers, you can enhance your download procedure to comply fully with the HTTP Protocol Specification. The specifications work with ranges to provide everything you need to resume interrupted downloads.

Here's how it works. First, a server sends the Accept-Ranges header in its initial response, if it supports letting the client resume downloads. The server also sends an entity tag header, ETag that contains a unique identification string.

The code below shows some of the headers that IIS sends back to the client in response to an initial download request, giving the client detailed information about the requested file.

   HTTP/1.1 200 OK
   Connection: close
   Date: Tue, 19 Oct 2004 15:11:23 GMT
   Accept-Ranges: bytes
   Last-Modified: Sun, 26 Sep 2004 15:52:45 GMT
   ETag: "47febb2cfd76c41:2062"
   Cache-Control: private
   Content-Type: application/x-zip-compressed
   Content-Length: 2844011
After receiving those headers, if the download is interrupted, Internet Explorer sends the ETag value back to the server with a subsequent download request, along with the Range header. The following code shows some of the headers that Internet Explorer sends to the server in an attempt to resume a broken download.

   GET HTTP/1.0
   Range: bytes=822603-
   Unless-Modified-Since: Sun, 26 Sep 2004 15:52:45 GMT
   If-Range: "47febb2cfd76c41:2062"
These headers show that Internet Explorer caches the entity tag provided by IIS and sends it back to the server in the If-Range header, which is one way to make sure that the download resumes with the exact same file. Unfortunately, not all browsers work exactly the same way. Other HTTP headers that a client might send to verify the file are If-Match, If-Unmodified-Since or Unless-Modified-Since. Apparently, the specification isn't perfectly clear on whether client software must support such headers, or which ones they must use. Therefore, some clients don't use any at all, IE only uses If-Range and Unless-Modified-Since. It's best to have your code check all of them. That way, your application can comply with HTTP at a very high level and work with multiple browsers. The Range header indicates the requested byte range—in this case the starting point from which the server should resume streaming the file.

When IIS receives a resume-download type request, it sends back a response that includes the headers shown below.

   HTTP/1.1 206 Partial Content
   Content-Range: bytes 822603-2844010/2844011
   Accept-Ranges: bytes
   Last-Modified: Sun, 26 Sep 2004 15:52:45 GMT
   ETag: "47febb2cfd76c41:2062"
   Cache-Control: private
   Content-Type: application/x-zip-compressed
   Content-Length: 2021408
Note that the preceding code has a different HTTP response code than the original download request—206 for the resume-download request vs. 200 for the initial download request. This indicates that the content about to come through the line is a partial file. This time, the Content-Range header specifies the exact amount and position of the bytes delivered.

Internet Explorer is very picky about these headers. If the initial response doesn't contain an ETag header, IE will never even try to resume a download. Other clients I tested didn't use the ETag header, they simply relied on the file name, requesting ranges and using the Last-Modified header (if they tried to verify the file at all).

The HTTP Protocol, a Step Further
The headers shown in the preceding section are sufficient to make the resuming-solution work. But that would not cover the HTTP specification entirely.

The Range header is capable of asking for more than one range in one single request, a feature called "multipart ranges." Don't confuse this with segmented downloading, which almost all downloading tools use to increase the speed of the download. These tools claim to improve download speed by opening two or more simultaneous connections, each of which requests a different range of the file.

The multipart ranges idea doesn't open multiple connections, but it does let client software request, for example, the first ten and last ten bytes of a file in a single request/response cycle.

To be completely honest, I could not find a single piece of software which really makes use of this feature. But I refused to write a disclaimer into my code reading "this is not fully HTTP compliant." Murphy's Law would surely spring into action if that feature were left out. However, multipart ranges are used in e-mail transfers, separating headers, plain text and attachments.

The Sample Code
Using this knowledge of how the client and server exchange header information to enable resumable downloads in conjunction with the idea of streaming chunks of a file at a time, you can add robust download management capability to your ASP.NET applications.

The way to achieve control over the download process is to intercept the download requests from the client, read the headers, and respond appropriately. Before .NET, you would have had to write an ISAPI (Internet Server API) application to accomplish this, but the .NET framework provides an IHttpHandler interface which, when implemented in a class, lets you intercept and handle requests using only .NET code. This means that your application will have total control and responsibility over the download process, IIS's automatic functionality will no longer be involved or available.

The sample code contains a custom HttpHandler class (called ZIPHandler) in the file HttpHandler.vb. ZipHandler implements IHttpHandler, and handles requests for .zip files.

To test the sample code, create a new virtual directory in IIS and copy the source files there. Create a file called download.zip and place it in that directory, too (note that IIS and ASP.NET cannot handle downloads larger than 2 GB, so make sure your file doesn't exceed that size). Configure your IIS virtual directory to map the .zip extension through aspnet_isapi.dll as described here.

Thanks for your registration, follow us on our social networks to keep up-to-date