Skip navigation.

Book Review: Agile Principles, Patterns, and Practices in C#All recent postsVisual Studio 2005 Service Pack 1 Beta: First Impression

Downloading Files Without Timeouts

Joe Stagner has recently published an MSDN article Build Smarter ASP.NET File Downloading Into Your Web Applications. Seems like a boring topic, but there’s an important lesson to learn: if you’re streaming a large file via Response.BinaryWrite or some similar technique, there’s a good chance the runtime will kill the request, and the download will abort (with a somewhat ugly message in the user’s browser).

In ASP.NET 2.0 the execution time out is 110 seconds, which is “the maximum number of seconds that a request is allowed to execute before being automatically shut down by ASP.NET.”

In his article Joe suggests to “chunk” files, i.e. stream them in small pieces (see listing 5). He claims it works, and I’m not sure why the runtime doesn’t consider such a thread for recycling. But the problem is the same thread is busy with relatively slow IO operations. The page still executes synchronously.

Asynchronous Stream Operations

And then I remembered that streams supported asynchronous read and write methods. My first stab at it was this:

response.AddHeader (
     "Content-Disposition", 
     "attachment; filename="" + file.Name +""");

response.ContentType = file.ContentType;
response.StatusCode = 200;

response.OutputStream.BeginWrite (
   data,
   0,
   (int) file.Size,
   delegate (IAsyncResult ar)
    {
      response.OutputStream.EndWrite (ar);
      response.OutputStream.Close ();
    },
  null);

Basically, a file download is kicked off asynchronously and finished by an anonymous delegate.

Didn’t work. A cusomer of ours was still experiencing time-outs.

I don’t feel like spelunking with Reflector through the framework code, but a page needs to know that it’s running a task asynchronously to be exempt from recycling. There’s nothing in the code above to alert the page of that.

Asynchronous Tasks

Then I remembered Asynchronous Pages in ASP.NET 2.0 by Jeff Prosise. Asynch pages and HttpHandlers are largely overlooked, and the most common example you’ll find is that of calling a web service asynchronously, catching a result and displaying it on the page. Folks, c’mon, more real-life examples, please!

If you bother to take a look at Jeff’s article, scroll down to Asynchronous Tasks. Asynch tasks are my favorite approach because

  1. AddOnPreRenderCompleteAsync is fugly, and
  2. RegisterAsyncTask flows impersonation, culture, and HttpContext.Current to the End and Timeout methods.

I can’t stress enough how convenient it is to reference the same context through an asynch call instead of hacking it. You can have your cake and eat it too!

My next take employed RegisterAsyncTask as you can see here (downloader refactored into a separate class).

Problem Solved?

It appears so. Asynchronous Web Forms are a big grey area in documentation, but from what I read it seems such a task should begin on one thread and, if necessary, finish on another. This is why I sweat propagation of HttpContext so much. A page with one or more asynch tasks should not time out (unless you pass a timeout callback in RegisterAsyncTask). Correct me if I’m wrong.

Brownie Points: Asynch HttpHandlers

You can achieve something very similar with an asynch HttpHandler. I haven’t tried it, but I don’t see what would stop you from kicking off a download in BeginProcessRequest and closing the stream in EndProcessRequest.

Conslusion

A seemingly boring chore gave me a great learning experience. I doubt I would ever consider file downloads facilitated by “ordinary”, synchronous pages. There are too many unknows, the worst being slow connectivity on the user’s end. I don’t know if even “chunking” would help a slow connection.

Comments

Comment permalink 1 intersense |
Very good idea.

Recently I have been confused by a web project for supporting file download. When several files are downloaded at the same time, some one may wait for a long time to start. But when it starts, the speed is very good.

I want to let our users start download as soon as possible, no matter the speed may be a little slower.

I have tried your method but it seems not work.

In your opinion,what should I do to solve my problem?
Comment permalink 2 intersense |
and I wander whether Asynchronous method will make HTTP download better experience?

better response speed, even slower speed?
Comment permalink 3 Ady |
Thanks for the insight.

Odly if you place code in the App_Code folder you do not get a timeout, but moving the code into assembly you do. I struggled with this for a long time until I came across this article.

After implementing an Asynch HttpHandler as suggested my downloads work perfectly.

Many Thanks,
Ady

Emails and Notifications

Would you like to be notified when somebody responds to this post?  Would you like to have these comments emailed to you?

TrackBacks

Sorry, TrackBacks are not allowed.

Submit your comment

Please enter only text since all HTML tags except hyperlinks will be stripped. Hyperlinks will become live links. Any comments with flaming or offensive language will be deleted. Be courteous to other posters. Thank you.

Your name (required):
Your email (optional):
Your site's URL (optional):
Enter this number
Type in the number above:
Comment (required):