views:

77

answers:

4

I'm downloading text from a website and I want to have the user be able to save it as a file. So, I have the following code that does just that.

protected void DownloadFile(string fileName, string content)
{
    Response.Clear();
    Response.ClearContent();
    Response.ClearHeaders();
    Response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
    Response.ContentType = "text/plain";
    Response.Write(content);
    Response.End();
}

The problem I am having is that I get an exception after this code runs. I believe it's due to the fact that I call Response.End(). So, every time a user downloads a file it redirects them to the generic error page because all generic exceptions redirect to that page.

Any ideas on how I can write text out to a file and not get this error? If I remove Response.End() I get my text and then the rest of the HttpResponse text, but I don't get the error.

Thanks.

A: 

You can catch the exception and do nothing in the catch()

CyberDude
That's how I handle it right now, but I prefer to not eat exceptions if I don't have to. So, I'm just trying to find a different/better way to accomplish this.
Dan H
This is absolutely terrible advice.
senfo
Are you doing anything after calling DownloadFile()? I used Response.End() myself but never had problems with exceptions. What particular exception is it anyway? ThreadAbortException?
CyberDude
NEVER eat an exception without at least logging it somewhere. At the very least, use Elmah (http://code.google.com/p/elmah/) to log the exception to the database.
David Lively
Yeah it's a ThreadAbortException. I guess I could catch that exception specifically...but it seems goofy to do that.
Dan H
ThreadAbortException is expected to be thrown when you use Response.End, that's the way it works. What use would you have by logging it? I didn't suggest you should generally eat exceptions, far from it, but in this case it's an expected behavior to get this exception.
CyberDude
+1  A: 

First, if this code is inside a .ASPX file, you need to move it out to a .ASHX file. Second, after you've moved to .ASHX you can simply write to the output stream and be done, you shouldn't need a Response.End();

This is the link I started with: http://dotnetperls.com/ashx-handler -- there isn't much to it. It simply removes a bunch of the asp.net "page" overhead.


Ideally you should have a page with a list, and a link/button to your "download.ashx" file. Then pass it a record id on the query string so it can do the lookup and response.write calls.

Nate Bross
It's in a base class of the aspx.cs file but if by moving this to an ASHX file would resolve the issue then I'd like to know how to do that. Can you provide an example?
Dan H
Posted the link I used, when I started learning about ASHX handlers.
Nate Bross
I'm not sure this will work. Here's the page...it has a list of files the user can download. There is a download button next to each files information. The content of the file exists in our database, not an actual file so I need to pass the content as a string. I need the page the user is viewing to stay and not be overwritten, and by using the HttpHandler it will overwrite the page...unless I'm doing it wrong. Know what I mean?
Dan H
A: 

Maybe you want to call Response.Output.Write() instead?

Hogan
A: 

Don't call Response.End(). There is still code to be run server-side after you have populated the headers whether you intend to or not. Instead consider using Response.Flush() and allowing the page to complete. Another alternative would be to use a HttpHandler (ashx) file instead of an aspx/ascx file. This will give you more basic access to the Response so you should be able to end without repercussion.

Joel Etherton
Using Response.Flush() doesn't help because it allows for the rest of the response to be written to my file. I just want the text I give it to be written to the file. I am looking into using an HttpHandler but it looks like using that will remove the content on the page and then display the download file dialog. I need the content on the page to stay.
Dan H
@Dan H - You could try overriding the page's render event to eliminate anything the page might write beyond the flush.
Joel Etherton
Got it! I did an override on the Render event and stopped it from continuing if the Response.ContentType == "text/plain". That works for us because all of our pages are "text/html".protected override void Render(HtmlTextWriter writer){ if (Response.ContentType != "text/plain") { base.Render(writer); }}Thanks!
Dan H