I recently had this exact same problem and I believe my solution (based on this answer to a related problem) is what you were looking for with this question. The basic idea is to get a progress spinner (in my case) to show while the file is being generated, hiding the progress spinner when the file is finished generating, and then providing the file to download. To achieve this I needed four things:
First, the action on the controller needs to store the file in the Session
[HttpPost]
public ActionResult RunReport(ReportViewModel viewmodel)
{
// Process that generates the object while will become the file here
// ...
using (var stream = new MemoryStream())
{
// Convert the object to a memory stream
report.Generate(stream); // Use your object here
string handle = Guid.NewGuid().ToString();
Session[handle] = stream.ToArray();
return new JsonResult()
{
Data = new
{
FileGuid = handle,
MimeType = "application/pptx",
FileName = "My File.pptx"
}
};
}
}
The controller also needs a new action which will provide the actual download file
public ActionResult Download(string fileGuid, string mimeType, string filename)
{
if(Session[fileGuid] != null)
{
byte[] data = Session[fileGuid] as byte[];
Session.Remove(fileGuid); // Cleanup session data
return File(data, mimeType, filename);
}
else
{
// Log the error if you want
return new EmptyResult();
}
}
Next, an AJAX call from the view which shows the progress spinner, calls RunReport (the action that takes a long time), uses the JSON array it returns to return the download file (which is a quick action), and then hides the spinner again.
<script type="text/javascript">
function RunReport(reportUrl) {
$.ajax({
cache: false,
url: reportUrl,
type: "POST",
success: function(response) {
window.location = "/Report/Download?fileGuid=" + response.FileGuid +
"&mimeType=" + response.MimeType + "&filename=" + response.FileName;
$("#progress-spinner").hide();
}
});
$("#progress-spinner").show();
}
</script>
Finally, the link that launches it all and generates the action link for use in the AJAX call
<a href="javascript: RunReport('@Url.Action("RunReport", "UserReport", new { ReportId = Model.Id })')">Run Report</a>
I hope this helps someone!