[Coding] Writing a Download File Script Page (ASP.NET)
Posted by Khatharsis on March 31, 2014
In my rather small application, one of the requirements is to provide a couple of different types of files for the user to download. Specifically, .xls and .xml files. The common question of how to prompt the user to download the file, rather than attempt to load it in the browser, was not an easy question to find the answer to. Likewise, finding the “proper” way to do it was another challenge.
Below is the code I eventually settled on:
Response.ContentType = "text/xml";
Response.AppendHeader("Content-Disposition", "attachment; filename=\"" + filename + "\"");
Response.TransmitFile(fullPath);
Response.Flush();
Response.SuppressContent = true;
Context.ApplicationInstance.CompleteRequest();
Let’s break it down.
Response.ContentType = "text/xml";
This is the property that tells the browser what kind of file you will be transmitting. In this case, it is an XML file. Be sure to look up the appropriate header for your file.
Response.AppendHeader("Content-Disposition", "attachment; filename=" + filename);
This method tells the browser that you have an attachment with a specific filename. In other words, this is what prompts the user to download the file. The filename you provide is what will show up as the default value in the Save As dialogue.
Response.TransmitFile(fullPath);
This method will actually do the transmitting of the file from the server to the user’s computer. (And actually this is what invokes the Save As dialogue.)
Response.Flush();
Response.SuppressContent = true;
Context.ApplicationInstance.CompleteRequest();
This chunk of code is a bit tricky. I originally did not have Flush() or the SuppressContent property lines in my code and it was still working fine. However, that was when I was transmitting a .xls file. When I was testing the code with a XML file, I found that the page’s HTML was being appended to the end of the file. Not quite what I wanted.
One solution was to use Response.End(), but this throws ThreadAbortException. I’m not expecting this app to require more than a few downloads a year, but it seemed like unnecessary overhead. Not to mention, StackOverflow articles generally point to avoid-unless-absolutely-required use. CompleteRequest() was the safest route.
Another solution was to transmit the file during the PreRender phase, but that still appended HTML to my XML file.
Finally, the SuppressContent property solution actually worked and seemed less dangerous than Response.End(). Setting it to true will stop HTTP content from being sent to the client, which also means, it is not being appended to my XML file.
As always, these are solutions I’ve found that work and appear to be less risky to use. I haven’t researched in great depth to be the authoritative figure and suggest/insist things be done this particular way (for example, I’m not sure if Flush() is necessary). Take with a grain of salt, but hopefully this will help answer that dreaded “How do I prompt my users to download this file?” question in a more thought-out manner.