Search code examples
node.jsnestjspdfkitnode-streams

Download a file from NestJS (without saving it )


I want to make client download a pdf file generated from my back-end. I am using the pdfkit-lib to generate my pdf file. From the NestJs documentation, I decided to use StreamableFile from a DuplexStream (pdfkit-lib works with a WritableStream and StreamableFile with a ReadableStream) :

@Get('/generate-file')
generateFile(): StreamableFile {
  const fileStream = new stream.Duplex();
  const doc = new pdfkit()
  doc.pipe(fileStream);
  // do stuff
  doc.end();
  return new StreamableFile(fileStream);
}

But I end up with this error :

Error: The _write() method is not implemented
    at new NodeError (node:internal/errors:371:5)
    at Duplex.Writable._write (node:internal/streams/writable:588:11)
    at writeOrBuffer (node:internal/streams/writable:389:12)
    at _write (node:internal/streams/writable:330:10)
    at Duplex.Writable.write (node:internal/streams/writable:334:10)
    at PDFDocument.ondata (node:internal/streams/readable:754:22)
    at PDFDocument.emit (node:events:390:28)
    at PDFDocument.Readable.read (node:internal/streams/readable:527:10)
    at flow (node:internal/streams/readable:1012:34)
    at resume_ (node:internal/streams/readable:993:3)

It can make it works with fs.createWriteStream and fs.createReadStream but I don't want to create the file on my server (and there is no callback on StreamableFile to delete). Any idea ?


Solution

  • It should be possible to do this without the overhead of writing to a temporary file. You can try streaming directly into the res stream, something like:

    @Get('/generate-file')
    generateFile(@Res() res: Response): void {
       const filename = 'SomeFileName.pdf';
       const doc = new PDFDocument({ bufferPages: true });
       const stream = res.writeHead(200, {
        'Content-Type': 'application/pdf',
        'Content-disposition': `attachment;filename=${filename}.pdf`,
       });
       doc.on('data', (chunk) => stream.write(chunk));
       doc.on('end', () => stream.end());
    
       doc
         .font('Times-Roman')
         .fontSize(12)
         .text(`just a test`);
       doc.end();
     }