The accepted answer didn't quite work for me as I got a 502 Bad Gateway result from the ajax call even though everything seemed to be returning fine from the controller.
Perhaps I was hitting a limit with TempData - not sure, but I found that if I used IMemoryCache instead of TempData, it worked fine, so here is my adapted version of the code in the accepted answer:
public ActionResult PostReportPartial(ReportVM model){
// Validate the Model is correct and contains valid data
// Generate your report output based on the model parameters
// This can be an Excel, PDF, Word file - whatever you need.
// As an example lets assume we've generated an EPPlus ExcelPackage
ExcelPackage workbook = new ExcelPackage();
// Do something to populate your workbook
// Generate a new unique identifier against which the file can be stored
string handle = Guid.NewGuid().ToString();
using(MemoryStream memoryStream = new MemoryStream()){
workbook.SaveAs(memoryStream);
memoryStream.Position = 0;
//TempData[handle] = memoryStream.ToArray();
//This is an equivalent to tempdata, but requires manual cleanup
_cache.Set(handle, memoryStream.ToArray(),
new MemoryCacheEntryOptions().SetSlidingExpiration(TimeSpan.FromMinutes(10)));
//(I'd recommend you revise the expiration specifics to suit your application)
}
// Note we are returning a filename as well as the handle
return new JsonResult() {
Data = new { FileGuid = handle, FileName = "TestReportOutput.xlsx" }
};
}
AJAX call remains as with the accepted answer (I made no changes):
$ajax({
cache: false,
url: '/Report/PostReportPartial',
data: _form.serialize(),
success: function (data){
var response = JSON.parse(data);
window.location = '/Report/Download?fileGuid=' + response.FileGuid
+ '&filename=' + response.FileName;
}
})
The controller action to handle the downloading of the file:
[HttpGet]
public virtual ActionResult Download(string fileGuid, string fileName)
{
if (_cache.Get<byte[]>(fileGuid) != null)
{
byte[] data = _cache.Get<byte[]>(fileGuid);
_cache.Remove(fileGuid); //cleanup here as we don't need it in cache anymore
return File(data, "application/vnd.ms-excel", fileName);
}
else
{
// Something has gone wrong...
return View("Error"); // or whatever/wherever you want to return the user
}
}
...
Now there is some extra code for setting up MemoryCache...
In order to use "_cache" I injected in the constructor for the controller like so:
using Microsoft.Extensions.Caching.Memory;
namespace MySolution.Project.Controllers
{
public class MyController : Controller
{
private readonly IMemoryCache _cache;
public LogController(IMemoryCache cache)
{
_cache = cache;
}
//rest of controller code here
}
}
And make sure you have the following in ConfigureServices in Startup.cs:
services.AddDistributedMemoryCache();