Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Long running process that will return a file

Tags:

asp.net-mvc

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:

  1. Action method gets called
  2. New thread started to generate process
  3. Return a View that tells the user the (pdf) is being generated
  4. Use AJAX to call the server and ask for progress
  5. Once finished, present the file to the user for download.

The parts I don't fully understand are:

  • The management of the thread across separate AJAX calls. I will possibly need some way of finding the running thread and requesting a status. Is there a static context I can keep a reference to the thread in? I'm aware of the Data Caching in HttpContext.Application, would that be suitable for this?
  • And how to present the completed file. Do I create a temp file and present a download link? Or can I make a final AJAX call that returns the file?
like image 476
jeef3 Avatar asked Aug 24 '09 22:08

jeef3


2 Answers

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.

like image 74
jeef3 Avatar answered Sep 21 '22 15:09

jeef3


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?

like image 24
Dmytrii Nagirniak Avatar answered Sep 22 '22 15:09

Dmytrii Nagirniak