TridentTrue
TridentTrue

Reputation: 193

Send file to client after AJAX response

I have a controller action to create a DataTable using filtered data sent from the client via AJAX, which is working fine on its own:

[HttpPost]
public JsonResult RetrieveStaffData(FilterModel jsonData)
{
    var data = (from i in db.StaffList
                where i.Username.Contains(jsonData.Username)
                    && i.Surname.Contains(jsonData.Surname)
                    && i.Forename.Contains(jsonData.Forename)
                    // etc...
                orderby i.Surname ascending)
                .ToList();

    if (data == null)
        return Json(new { success = "False", message = "filter brought back no results" });

    DataExport(ToDataTable(data));

    return Json(new { success = "True", message = "data retreival successful" });
}

I also have an existing DataExport controller action which converts a DataTable to an excel document and sends it to the client for download, on its own this works fine as well:

public void DataExport(DataTable dt)
{
    Response.Clear();
    Response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
    Response.AddHeader("content-disposition", "attachment;filename=spreadsheetname.xlsx");

    using (var p = new ExcelPackage())
    {
        var ws = p.Workbook.Worksheets.Add("StaffList");
        ws.Cells.LoadFromDataTable(dt, true);
        ws.Cells.AutoFitColumns();

        var ms = new System.IO.MemoryStream();
        p.SaveAs(ms);
        ms.WriteTo(Response.OutputStream);
    }

    Response.OutputStream.Flush();
    Response.OutputStream.Close();
    Response.End();
}

The problem is that when the DataExport action is called before the return the file is sent to the client as the AJAX response instead of a file download, which comes out as garbled binary in the response.

How can I send the client a response message and then send the file for download afterwards? Would Async/Await help here? I think I can use Blobs in JS to handle the file clientside but just wondering if there's a better way to do it.

Thanks in advance for any help that can be provided.

Upvotes: 0

Views: 641

Answers (1)

ADyson
ADyson

Reputation: 61849

You have to get the client to make a secondary request, and not via ajax - you can't download a file via ajax. Use something like window.open, directed to the URL of the a Download action method, which runs the export method.

I don't know how you've coded your ajax request, but in the callback which indicates a successful response, do something along the lines of:

window.open(@Url.Action("DownloadData"));

It's also possible, looking at your code, that you don't need the ajax request at all - all it seems to return is a "yes/no" type response. Perhaps the page should simply open the window to the "Download" action directly, which would run the query and then the DataExport function.

Upvotes: 1

Related Questions