views:

347

answers:

3

I've created a page that allows users to download a file when they click a button... the button's onclick event is linked to the following chunk of code:

this.Page.Response.Clear();
this.Page.Response.ContentType = System.Net.Mime.MediaTypeNames.Application.Zip;
this.Page.Response.AppendHeader("Content-Disposition", "attachment; filename=\"" + System.IO.Path.GetFileName(filename) + "\"");
this.Page.Response.TransmitFile(filename);
this.Page.Response.Flush();
this.Page.Response.End();

The download works fine, but now when I try to interact with the page, (for instance, hit the download button again), nothing posts back.

Am I responding to the download request incorrectly (should I be using a different/new response object), or is there something else I need to do to make the page active after the download?


Edit:

So I've tried encorporating the two posters suggestions for creating a httphandler, and calling a Response.Redirect to the handler from the button's click event.

    void submitButton_Click(object sender, EventArgs e)
    {
        label.Text = "Boo!";
        this.Page.Response.Redirect("~/ViewAttachment.ashx?id=foo", false);
    }

If I step through this on the debugger, it continues after the redirect call, but the page just returns to a state where the button doesn't work, and the labels have their default values. Am I now doing the redirect wrong?

A: 

By using Response.Clear() you've killed the headers. While there may appear to be valid html markup in the browser there really isn't. The headers are gone, so the controls that appear to be still rendered aren't valid. If you click "back" you should be able to repost information. What you probably want to do is make this call in a different window using PostBackUrl in your button click and setting the target of your form to a different window (then using SetTimeout to reset it to _self).

Joel Etherton
+1  A: 

If using another handler an option?

Here's a slimmed down version of what I've used before (suggestions welcome this was a quick write). The HttpHandler (AttachmentFile is just a class with the blob data and some attributes collected when the file was uploaded):

public class AttachmentHandler : IHttpHandler
{
  public const string QueryKeyID = "ID";

  public void ProcessRequest(HttpContext context)
  {    
    var r = context.Response;
    var attachmentID = context.Request.QueryString[QueryKeyID];

    Attachment a = DataContext.GetById<AttachmentFile>(attachmentID);

    r.ContentType = a.ContentType;
    r.AppendHeader("Content-Type", a.ContentType);
    r.AppendHeader("content-disposition", string.Format("attachment; filename=\"{0}{1}\"", a.AttachmentName, GetExtension(a.FileName)));
    r.BufferOutput = false; //Stream the content to the client, no need to cache entire streams in memory...
    r.BinaryWrite(a.BlobData);
    r.End();
  }

  private static string GetExtension(string fileName)
  {
    if(fileName.IsNullOrEmpty()) return string.Empty;
    var i = fileName.LastIndexOf(".");
    return i > 0 ? fileName.Substring(i) : string.Empty;
  }
}

In the web.config:

<system.web>
  <httpHandlers>
    <add verb="*" path="ViewAttachment.ashx" type="MyNamespace.AttachmentHandler, MyWebDllName"/>
  </httpHandlers>
</system.web>

Then all you do is render a link in the form of ~/ViewAttachment.ashx?ID=5 on the page, clicking the button will download the file, but not mess with your page's lifecycle at all.

Now there are more considerations, like security and such....I trimmed them out for this example. I'm also using this style of link: ~/Attachment/{id} via webforms routing...if either of these interest you I'll update to include them. Or you could hate this approach all-together...just letting you know it's an option.

Nick Craver
Ok, so I think I've mostly implemented what you suggested, but I still get this behavior causing it to not respond after redirecting to another page. Did you have something else in mind?
tbischel
@tbischel - Don't redirect :) Just make that button be a hyperlink to that url and you're all set.
Nick Craver
I must be crazy... I've set the postback url of the button to the page... same problem. Do I actually create a hyperlink and drop a button control in it?
tbischel
@tbischel - Do something like this: `<asp:HyperLink runat="server" NavigateUrl="~/ViewAttachment.ashx?ID=5">Click Me</asp:HyperLink>`...or give it an ID and set the NavigateUrl from the code-behind.
Nick Craver
+1  A: 

You are mostly there.

All you need to do in your button click event handler is Response.Redirect to another ASPX page. Put that code your wrote on the other page.

Then everything will work just as you expect. The browser will actually stay on the original page and not go to the new page because you have set the content disposition to attachment - which is great.

mike nelson
Am I doing the redirect wrong? It still doesn't work. see the edit for details...
tbischel