Reputation: 219
I need to make a pdf file with user content and send it back. I chose pdfmake, because then can make a tables. I use Koa.js;
router.post('/pdf', koaBody(), async ctx => {
const doc = printer.createPdfKitDocument(myFunctionGeneratePDFBody(ctx.request.body));
doc.pipe(ctx.res, { end: false });
doc.end();
ctx.res.writeHead(200, {
'Content-Type': 'application/pdf',
"Content-Disposition": "attachment; filename=document.pdf",
});
ctx.res.end();
});
And get an error
Error [ERR_STREAM_WRITE_AFTER_END]: write after end
at write_ (_http_outgoing.js:572:17)
at ServerResponse.write (_http_outgoing.js:567:10)
at PDFDocument.ondata (_stream_readable.js:666:20)
at PDFDocument.emit (events.js:182:13)
at PDFDocument.EventEmitter.emit (domain.js:442:20)
at PDFDocument.Readable.read (_stream_readable.js:486:10)
at flow (_stream_readable.js:922:34)
at resume_ (_stream_readable.js:904:3)
at process._tickCallback (internal/process/next_tick.js:63:19)
But save in intermediate file and send its work ...
router.post('/pdf', koaBody(), async ctx => {
await new Promise((resolve, reject) => {
const doc = printer.createPdfKitDocument(generatePDF(ctx.request.body));
doc.pipe(fs.createWriteStream(__dirname + '/document.pdf'));
doc.end();
doc.on('error', reject);
doc.on('end', resolve);
})
.then(async () => {
ctx.res.writeHead(200, {
'Content-Type': 'application/pdf',
'Content-Disposition': 'attachment; filename=document.pdf',
});
const stream = fs.createReadStream(__dirname + '/document.pdf');
return new Promise((resolve, reject) => {
stream.pipe(ctx.res, { end: false });
stream.on('error', reject);
stream.on('end', resolve);
});
});
ctx.res.end();
});
Upvotes: 1
Views: 7090
Reputation: 96
I just ran into the same issue and your question helped me out, and I've figured out the issue: you're ending the response/stream right away, while it isn't completely written yet. The problem is doc.end()
returns right away, before writing is finished. The trick is to let your doc end the stream when it's finished (so no more end: false
) and wait for this event, for example using a Promise.
The fixed code looks like this:
router.post('/pdf', koaBody(), async ctx => {
const doc = printer.createPdfKitDocument(myFunctionGeneratePDFBody(ctx.request.body));
doc.pipe(ctx.res);
doc.end();
ctx.res.writeHead(200, {
'Content-Type': 'application/pdf',
"Content-Disposition": "attachment; filename=document.pdf",
});
return new Promise(resolve => ctx.res.on('finish', resolve));
});
Upvotes: 6
Reputation: 3393
Avoid using writeHead
, see https://koajs.com/#response.
Do like this:
ctx.attachment('file.pdf');
ctx.type =
'application/pdf';
const stream = fs.createReadStream(`${process.cwd()}/uploads/file.pdf`);
ctx.ok(stream); // from https://github.com/jeffijoe/koa-respond
Upvotes: 1