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:
- Initiate a GET Request
- Query a database for relevant results
- Load data into an EJS template
- Save the resulting HTML
- Use the npm module HTML-pdf to stream it into the browser
Other important details:
- This file is meant to be ephemeral. I don't want to save it anywhere.
- I prefer not to even have to write it anywhere, since it's only meant to exist in the browser at that moment.
- 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.