Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

content-disposition does not cause a save as

I have the following code:

Response.ClearContent();
Response.AddHeader( "Content-type", "application/vnd.ms-excel");
Response.AddHeader("content-disposition", "attachment;filename=test.xls");
Response.ContentType = "application/excel";
var swr = new StringWriter();
var tw = new HtmlTextWriter(swr);
grd.RenderControl(tw);
Response.Write(swr.ToString());
Response.Flush();
Response.End();
tw.Close();
swr.Close();

This action is triggered from the following bit of jquery code:

   <img src="../../../../Content/images/Excel-icon.png" 
      onclick = "alert ($('#Filter').serialize());
                    $.ajax({
                         type: 'POST',
                         url: '<%=Url.Action( "Excel") %>',
                         data: $('#Filter').serialize(),
                         success : function (data, textStatus, jqXHR) 
                              { alert (data);},
                         error: function (jqXHR, textStatus, errorThrown)
                              { alert (textStatus + ' ' + errorThrown);}
                         });" />

I have confirmed with fiddler that the headers have the expected values. I can also see the data in fiddler using the Web View.

Also, when I show the contents of data from the success function, it has the raw html for the table that I am trying to export to excel.

Thanks in advance for your help.

like image 590
Nick Harrison Avatar asked Feb 22 '23 21:02

Nick Harrison


2 Answers

Oh, no, you cannot use AJAX to download files. The AJAX call is executed, the file is streamed to the client, your success callback is executed and passed the contents of the file that's where you will get stuck. For obvious security reasons you cannot do much with this data. You cannot save it to the client computer. You cannot show a prompt to Save As at this stage.

So remove your javascript AJAX call and invoke your controller action using a normal GET or POST request.

like image 169
Darin Dimitrov Avatar answered Feb 24 '23 09:02

Darin Dimitrov


I would suggest not putting that code directly in an Action method -- that's not a very clean way to use MVC, and you certainly can't trigger a file download from an AJAX request.

You could derive a new action result -- call it ExcelResult -- from the FileResult class, an override its ExecuteResult method to force the content into the Response.

The button click should simply hit the action through a link (navigate to it). The response from the server and the browser will work together to force the file at a user without browsing to a new page.

Example:

public class ExcelResult : FileResult
{
    public override void ExecuteResult(ControllerContext context)
    {
        string file;

        using (var swr = new StringWriter())
        {
            using (var tw = new HtmlTextWriter(swr))
            {
                // Not sure where your grd object comes from
                grd.RenderControl(tw);
                file = swr.ToString();
            }
        }

        var response = context.HttpContext.Response;

        response.Buffer = true;
        response.Clear();
        response.ClearHeaders();
        response.ContentType = "application/excel";
        response.CacheControl = "public";
        response.AddHeader("Pragma", "Public");
        response.AddHeader("Expires", "0");
        response.AddHeader("Cache-Control", "must-revalidate, post-check=0, pre-check=0");
        response.AddHeader("Content-Description", "describe your file");
        response.AddHeader("Content-Disposition","attachment; filename=excel.xls");
        response.Write(file);
        response.Flush();
        response.End();
    }

}

And your Action is simply:

public ExcelResult Excel()
{
    return new ExcelResult(/* whatever you need to pass */);
}
like image 39
Cᴏʀʏ Avatar answered Feb 24 '23 10:02

Cᴏʀʏ