I'm using ASP.NET MVC and have a long running process. Specifically I am generating a large PDF for the user to download.
I understand the basic concept:
The parts I don't fully understand are:
HttpContext.Application
, would that be suitable for this?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.
The Cashe is very well suitable for that. Only one thing is to make sure the item cached is never removed while the process is running (You can use ItemPriority.NotRemovable for that).
You can save the file on disk in a temp folder or you can keep it in cache for some time (it depends).
I personally don' like to pollute hard disk with files so I would keep the file in the cache (with MediumPriority for a couple of minutes). But if the file is large and can be generated often consider using a Database of file system instead.
On the client, when the last Ajax request returns result( can look like {progress: "100%", resultUrl: "http://your.url/Where/ToGet/TheFile.aspx?file=GUID-OR-CACHE-KEY"} ) you can redirect the browser to a URL provided.
It, in turn, will render that file as a binary result.
Client redirect can be done using Javascript like this:
location.href = response.resultUrl;
BTW, how do you generate PDF? NFOP?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With