Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Post Back does not work after writing files to response in ASP.NET

What I have?

I have an ASP.NET page which allows the user to download file a on a button click. User can select the file he wants from a list of available files (RadioButtonList) and clicks on download button to download it. (I should not provide link for each file that can be downloaded - this is the requirement).

What do I want?

I want the user to download multiple files one by one by selecting the required radio button and clicking on the button.

What problem am I facing?

I can download the file for the first time properly. But, after downloading, if I select some other file and click on the button to download it, click event of the button does not post back and the second file will not be downloaded.

I use the following code on the button click event:

protected void btnDownload_Click(object sender, EventArgs e)
{
    string viewXml = exporter.Export();
    Response.Clear();
    Response.AddHeader("Content-Disposition", "attachment; filename=views.cov");
    Response.AddHeader("Content-Length", viewXml.Length.ToString());
    Response.ContentType = "text/plain";
    Response.Write(viewXml);
    Response.End();
}

Am I doing something wrong here?

Same problem can be replicated in IE6, IE7 and Chrome. I think this problem is browser independent.

like image 550
Vijay Avatar asked Feb 25 '10 19:02

Vijay


4 Answers

I had this same issue with sharepoint. I have a button on the page that sends a file and after clicking the button, the rest of the form was unresponsive. Turns out it is a sharepoint thing that sets the variable _spFormOnSubmitCalled to true to prevent any further submits. When we send a file this doesn't refresh the page so we need to manually set this variable back to false.

On your button in the webpart set the OnClientClick to a function in your javascript for the page.

 <asp:Button ID="generateExcel" runat="server" Text="Export Excel" 
OnClick="generateExcel_Click" CssClass="rptSubmitButton"
OnClientClick="javascript:setFormSubmitToFalse()" />

Then in the javascript I have this function.

function setFormSubmitToFalse() {
    setTimeout(function () { _spFormOnSubmitCalled = false; }, 3000);
    return true;
}

The 3 second pause I found was necessary because otherwise I was setting the variable before sharepoint set it. This way I let sharepoint set it normally then I set it back to false right after.

like image 200
RobbZ Avatar answered Nov 02 '22 21:11

RobbZ


Offhand, what you're doing should work. I've successfully done similar in the past, although I used a repeater and LinkButtons.

The only thing I can see that's different is that you're using Response.Write() rather than Response.OutputStream.Write(), and that you're writing text rather than binary, but given the ContentType you specified, it shouldn't be a problem. Additionally, I call Response.ClearHeaders() before sending info, and Response.Flush() afterward (before my call to Response.End()).

If it will help, here's a sanitized version of what works well for me:

// called by click handler after obtaining the correct MyFileInfo class.
private void DownloadFile(MyFileInfo file) 
{
    Response.Clear();
    Response.ClearHeaders();
    Response.ContentType = "application/file";
    Response.AddHeader("Content-Disposition", "attachment; filename=\"" + file.FileName + "\"");
    Response.AddHeader("Content-Length", file.FileSize.ToString());
    Response.OutputStream.Write(file.Bytes, 0, file.Bytes.Length);
    Response.Flush();
    Response.End();        
}

You may want to consider transferring the file in a binary way, perhaps by calling System.Text.Encoding.ASCII.GetBytes(viewXml); and passing the result of that to Response.OutputStream.Write().

Modifying your code slightly:

protected void btnDownload_Click(object sender, EventArgs e)
{
    string viewXml = exporter.Export();
    byte [] bytes = System.Text.Encoding.ASCII.GetBytes(viewXml); 
    // NOTE: you should use whatever encoding your XML file is set for.
    // Alternatives:
    // byte [] bytes = System.Text.Encoding.UTF7.GetBytes(viewXml);
    // byte [] bytes = System.Text.Encoding.UTF8.GetBytes(viewXml);

    Response.Clear();
    Response.ClearHeaders();
    Response.AddHeader("Content-Disposition", "attachment; filename=views.cov");
    Response.AddHeader("Content-Length", bytes.Length.ToString());
    Response.ContentType = "application/file";
    Response.OutputStream.Write(bytes, 0, bytes.Length);
    Response.Flush();
    Response.End();
}
like image 38
Randolpho Avatar answered Nov 02 '22 19:11

Randolpho


A simple way to do this without removing Response.End is to add client-side js to do the page refresh. Add the js to your button's onclientclick property.

e.g.

    onclientclick="timedRefresh(2000)"

then in your html..

    <script type="text/JavaScript">
    <!--
    function timedRefresh(timeoutPeriod) {
        setTimeout("location.reload(true);",timeoutPeriod);
    }
    //   -->

like image 4
Digitz Avatar answered Nov 02 '22 20:11

Digitz


Remove Response.End() and let the response end naturally within the ASP.NET ecosystem.

If that does not work, I would recommend putting the button in a separate <form> and post the required data to a separate HTTP handler. Setup the HTTP handler to export the XML instead of a web page.

like image 3
Josh Stodola Avatar answered Nov 02 '22 19:11

Josh Stodola