It works!
Here's what I've done:
Step 1 & 2 - Action Method gets called, long running thread is started
When my action method gets called, it generates a unique ID. I then instantiate an instance of my PdfGenerator
class, create a new thread that calls PdfGenerator.Generate
and start it.
public class PdfGenerator
{
public string State;
public byte[] Data;
public void Generate()
{
// Generate PDF/Long running process
// Should update State as it goes
// ...
// Once finished, Data is populated with the binary byte[]
}
}
Once the thread has started (or before starting) the generator instance is stored in the cache:
HttpContext.Cache[guid] = generator;
I also attach the guid to the ViewData so that it can be reference in my view script.
Step 3 & 4 - Display and update status/progress view
Now that the thread is running and PDF generation has begun, I can display my progress view script. Using jQuery's $.getJSON
I am able to poll a separate Action to find the status of the generation:
[OutputCache(Duration = 0, VaryByName = "none", NoStore = true)]
public JsonResult CheckPdfGenerationStatus(string guid)
{
// Get the generator from cache
var generator = HttpContext.Cache[guid] as PdfGenerator;
if (generator == null)
return Json(null);
else
return Json(generator.State);
}
My view script interprets the Json and displays the appropriate progress information.
Step 5 - Present file to user
Once the generation is completed, the generators state is set accordingly and when jQuery receives this information, it can either make available a link, or directly send the file using javascripts location.href
.
The Action method that sets up and returns the file simply gets the generator out of the cache and returns the attached byte[]
public ContentResult DownloadPdf(string guid)
{
var generator = HttpContext.Cache[guid] as PdfGenerator;
if (generator == null)
return Content("Error");
if (generator.State == "Completed")
{
return Content(generator.Data);
}
else
{
return Content("Not finished yet");
}
}
My my actual work I've got more detailed state such as Initialised, Running and Completed. As well as a progress percentage (expressed as a decimal, 1.0 being complete).
So yeah, hope that helps anyone else trying to do something similar.