2

Okay, I see a few versions of this question out here, but I think mine goes a bit deeper than some of the others and I don't see satisfactory answers.

What I Want:

I have a page on a website that will have its contents frequently changed by users, and I want them to hit a button that says 'Generate PDF', and be able to turn the relevant information on the page into a nice looking PDF report.

I'm trying to do this with NodeJS, Express, with a deployment on Heroku.

Generically, this is how I'm trying to accomplish this:

MY PLAN:

  1. Initiate a GET Request
  2. Query a database for relevant results
  3. Load data into an EJS template
  4. Save the resulting HTML
  5. Use the npm module HTML-pdf to stream it into the browser

Other important details:

  1. This file is meant to be ephemeral. I don't want to save it anywhere.
  2. I prefer not to even have to write it anywhere, since it's only meant to exist in the browser at that moment.
  3. This app is deployed on Heroku, and need to work within its constructs. I prefer not to use a file store like S3.

MY CODE:

router.get('/formulas/:id/pdf', function(req, res){
  var db = req.db.collection('users');
  var id = new ObjectID(req.params.id);
  var pointer = {"formulas.$": 1, "_id": 0};
//query database, get the info out
db.aggregate([
  {$match: {"formulas.f_id": id}},
  {$unwind: "$formulas"},
  {$match: {"formulas.f_id": id}},
  {$project : {"formulas": 1, "_id": 0}}
]).toArray(function(e, doc){
  if (e) {
  throw e;
} else {
  var html = null;
  ejs.renderFile('./views/pdf.ejs', { //create template from db query
    project: doc[0].formulas,
    title: 'Formula Info Report',
    description:  'PDF Report For Your Formula by Nutraceutical Pro',
    ID: 'pdf',
    keywords: 'PDF, PDF generator, Formula Info Report',
    user: req.user,
    loggedIn: req.isAuthenticated()
  }, function(err, results){
    if (err) {
      console.log(err);
      }
      html = results; //save results of HTML output
    });
    var options = { format: 'Letter' };
    var path = './public/pdf/formula-' + req.params.id + '.pdf';
    pdf.create(html, options).toStream(function(err, stream) {//initiate stream
      if (err) {
        return console.log(err);
      }
      if (stream) {
        console.log(stream);
        stream.pipe(fs.createWriteStream(path));
        console.log("the pdf was streamed.");
        res.end();
        }
      });
    }
  });
});

I initiate the GET request with an AJAX call like so:

 $('.getPDF').click(function(){
   var filepath = 'https://my-domain.herokuapp.com/pdf/formula-' + this.id + '.pdf';
 $.ajax({
   url: '/formulas/'+ this.id +'/pdf',
   type: 'GET',
   success: function () {window.open(filepath);}
 });

When I run the script I generate a stream, and the console logs that the PDF was created. If I just save the resulting file to my local system it comes out fine, but in my app I get the following error message:

Cannot GET /pdf/formula-59cc38992fb99b00045832ad.pdf

So it's like the raw material for the PDF is being created, but it's not being logged and created in such a way that allows the app to read it.

I'm open to a variety of solutions for this, so if there are much easier ways to do it I'm all ears. Prefer to stick with my current stack though.

Jonathan Bechtel
  • 3,497
  • 4
  • 43
  • 73

0 Answers0