Downloading Files Without Timeouts
Posted in Development
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
AddOnPreRenderCompleteAsyncis fugly, and- “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.
6 comments
intersense
on December 2, 2006
and I wander whether Asynchronous method will make HTTP download better experience?
better response speed, even slower speed?
Ady
on March 4, 2008
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
Frank
on November 1, 2009
There's a problem with the code
http://aspnetresources.com/sourcecode/file_download/LocalDownloadServer.cs.aspx
On this line, you are downloading the entire file into memory.
byte[] data = FileBL.GetFileImage (file.PrimaryKey);
So for large files, you are going to exhaust memory on the web server. Is there a way to do this combining the async page approach with the "chunking" method you referenced at the beginning of the article?
Milan Negovan
on November 6, 2009
Frank, that's a great point. Indeed, that line of code reads a file into memory in its entirety. For very large files this may present problems.
If you're reading from the file system, I think using a Stream would be best.
As to the database, I'm not even sure. ADO.NET supports async operations, but it opens a whole can of worms: you have to use a separate connection and do a lot of other things to ensure that EVERYTHING runs asynchronously.
Rangel
on January 11, 2010
Hi,
I've tried all the solutions above and none of them worked.
The solution for me is to use the TransmitFile method.
Hope it helps!

intersense
on December 2, 2006
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?