Friday, June 3, 2011

How to Download File from inside an UpdatePanel in ASP.NET

PartialPageRendering when used with the UpdatePanel is a great thing in general. One problem comes when you want to mess with the Response and call things like End which is required when you return want to return a file instead of the page you were on. An example of this is a GridView with a Download button below it. If you are not using the UpdatePanel then there is no problem and clicking the Download button will all you to download the file as expected. There are lots of posts on the web on how to do this so I won’t go into the code. However, it involves calling things like Response.Clear(), Response.ContentType, Response.AddHeader(), Response.End().
Now let’s assume you put the GridView in an UpdatePanel. All those Response calls noted above are things that the PartialPageRendering and UpdatePanel don’t know what to do with because they were expected something like the page that made the request. So we need to work around the issue. I found some solutions on the internet, but didn’t really find them very easy or straight forward. So, I came up with my own.

Solution 1

One solution is to put the Download button outside the UpdatePanel. This tends to work best, but is not always possible.

Solution 2

If you can’t do solution 1, then consider faking it. What I mean is move the Download button which in my case is implemented as a LinkButton outside the UpdatePanel and hide it using css. Then add a Hyperlink inside the UpdatePanel. Now, on your page load you can set the hyperlink to have the same postback url as the LinkButton that is outside the UpdatePanel. That way when you click the hyperlink inside the UpdatePanel you are effectively doing the same thing as if you clicked the hidden LinkButton that is outside the UpdatePanel. Fairly simple.
Here is the snippets of code to help you see what I am talking about.
Here is the HyperLink that is inside the UpdatePanel and is also the button that you will be clicking in the Browser.
<asp:HyperLink ID="btnExportToExcelClicker" runat="server" Text="Export all rows to Excel" />

Here is the LinkButton that is invisible to the user and is outside the UpdatePanel
<asp:LinkButton ID="btnExportToExcel" runat="server" onclick="btnExportToExcel_Click" Text="Export all rows to Excel" style="display:none;" />

Here is the code-behind. In my code btnExportToExcelClicker is the button that is in the UpdatePanel and btnExportToExcel is the button that is outside the UpdatePanel. You will actually be clicking btnExportToExcelClicker because btnExportToExcel is hidden.
protected void Page_Load(object sender, EventArgs e)
{
    btnExportToExcelClicker.NavigateUrl = Page.ClientScript.GetPostBackClientHyperlink(btnExportToExcel, null);
}

This technique uses GetPostBackClientHperlink and can be expanded for other purposes and other controls. In some cases you may need to consider using GetPostBackEventReferences, though I haven’t tried it.

UPDATED: November 5, 2013

Solution 3

This is my new favorite solution due to its simplicity. This involves creating a new page in your application. Call it ReportDownload.aspx (or whatever you desire). It is critical that it not have an update panel. Best choice is usually to not even use a master page for this. The reason is that we are not going to use anything on the ReportDownload.aspx file. We will however change the ReportDownload.aspx.cs file. On the page load just put your download streaming code that would be something similar to the following:

var response = Page.Response;
response.Clear();
response.AddHeader("content-disposition", string.Format("attachement; filename=\"{0}\"", "Report.pdf"));
response.ContentType = "application/octet-stream";
response.WriteFile(@"c:\temp\DB.pdf");
response.End();

You just need to put this in the Load or PreRender events. To test this, just go to the new page you created in your favorite browser. You should see the file you specified in the code above. Now that you know that going to the url works, it is just a matter of using that url anywhere on a page (including in an UpdatePanel). I used it in LinkButton or HyperLink. You can pass a code (i.e. a record id, etc) in the url to generate the url. The nice thing is you can also incorporate security on the ReportDownload.aspx as well.

6 comments:

Anonymous said...

Completely and utterly does not work for me. Seemed like a good idea. I'll keep looking into what I might be doing wrong

Anonymous said...

Good, It worked for me

Anonymous said...

works great, cheers.

Sudhir said...

Great Work

Anonymous said...

Great article, great update!

MacSpudster said...

Just use a HyperLink with targeg="blank" to the download page that uses an encrypted querystring value of the filename and | and the current datetime (to make the encrypted URL querystring diff every time).
Then, on Download page code behind, decrypt the querystring and get the filename (first param in the decrypted querystring, split by the VertBar |) and then use the Response code...