I am using ASP.NET Core and ClosedXML to generate an Excel export.
Export Method 1
Here is the controller method that DOES return an .xlsx
file to the browser. The method gets called from a .cshtml
page:
<form id="downloadLoanEvents" asp-action="DownloadLoanEventsLog" asp-controller="Lender" asp-route-id="@Model.LoanFileId" method="post">
<div class="form-group">
<input type="submit" value="Submit" />
</div>
</form>
This is the DownloadLoanEventsLog
controller method:
[HttpPost]
public async Task<IActionResult> DownloadLoanEventsLog(string id)
{
var user = await _userManager.GetUserAsync(HttpContext.User);
var loan = _unitOfWork.LoanFiles.GetById(id);
var list = eventLogService.GetLoanFileEventLog(id, _unitOfWork, user);
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
string fileName = $"Loan-{loan.LoanNumber}-EventsLog.xlsx";
try
{
using (var workbook = new XLWorkbook())
{
IXLWorksheet worksheet = workbook.Worksheets.Add("Events");
worksheet.Cell(1, 1).Value = "EventDate";
worksheet.Cell(1, 2).Value = "LoanNumber";
int index = 2;
foreach (var row in list)
{
worksheet.Cell(index, 1).Value = row.EventDateTimeDisplay;
worksheet.Cell(index, 2).Value = row.LoanNumber;
index++;
}
using (var stream = new MemoryStream())
{
workbook.SaveAs(stream);
var content = stream.ToArray();
return File(content, contentType, fileName);
}
}
}
catch (Exception ex)
{
return RedirectToAction("CloseLoan", new { id = id }).WithWarning("", "Exporting Loan File Events Failed");
}
finally
{
}
}
The DownloadLoanEventLog
method works great. I quickly get an expected Excel file to the browser.
Export Method 2
Here is the controller method that DOES NOT return an .xlsx
file to the browser.
The method is nearly identical to the 1st method, but this export kicks off in Ajax call to send a json array of data to the controller.
var rows = $("#report").jqxGrid('getrows');
var loans = [];
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
loans.push({
"loanNumber": row.loanNumber,
"loanType": row.loanType,
});
}
var jqxhr = $.ajax({
data: JSON.stringify(loans),
contentType: "application/json",
url: "/Reports/ExportLoansReport",
type: "POST"
});
This is the ExportLoansReport
controller method:
[HttpPost]
public IActionResult ExportLoansReport([FromBody] List<LoansReportExportViewModel> model)
{
string contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
string fileName = $"Loans-Export.xlsx";
try
{
using (var workbook = new XLWorkbook())
{
IXLWorksheet worksheet = workbook.Worksheets.Add("Loans");
worksheet.Cell(1, 1).Value = "LoanNumber";
worksheet.Cell(1, 2).Value = "LoanType";
int index = 2;
foreach (var row in model)
{
worksheet.Cell(index, 1).Value = row.LoanNumber;
worksheet.Cell(index, 2).Value = row.LoanType;
index++;
}
using (var stream = new MemoryStream())
{
workbook.SaveAs(stream);
var content = stream.ToArray();
return File(content, contentType, fileName);
}
}
}
catch(Exception ex)
{
return Json("some fail msg");
}
}
ExportLoansReport
gets a valid json object in the [FromBody]
parameter, and the code does not throw any exception at all. Once return File
fires, nothing happens, no file is returned to the browser.
I suspect this issue is because the request was made from the client side but I am not sure how to fix it.
2
Answers
What I ended up doing was creating another controller method that got called from JS in the cshtml file that returned the FileResult.
JS to download a file export:
The method to create the file:
Above method creates the file, saves to a directory, and returns success to the javascript function.
Then, if the javascript gets a success back, then the "DownloadExportReport" gets called to receive the file in the browser.
Thanks for all who helped.
It seems that ajax itself is not designed to support downloading file scenarios. You might want to have a try with
ajaxfiledownload
package which supports fetching files from server in an asynchronous way.