Search code examples
node.jsexpresspdfwkhtmltopdf

wkhtmltopdf response in a buffer


So, I have written a small code to convert the html string in to pdf using the library called Wkhtmltopdf

Here is the code I have written:

index.js

const express = require('express')
const app = express()
app.use(express.json())
const port = 3000
const compiledHtml = '<h1> This is testing </h1>'
app.post('/gc', async (req, res) => {
   wkhtmltopdf(compiledHtml, {
      pageSize: 'A4',
      orientation: 'Landscape',
      marginLeft: '1mm',
      marginTop: '1mm'
   }).pipe(fs.createWriteStream('out.pdf')) //--->(A)
   console.log('All done')
   res.send(compiledHtml)
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))

In this code, I have created REST call, POST in nature that generates the pdf and saves in a file named out.pdf (equation (A), above) in the same folder where the script is running.

But in reality I want to generate the pdf in memory, gets the buffer of the same and return the buffer back to the client so that the browser start downloading the file, I have a code as well for that, here is the code to return the buffer in response in express

snippet

......
......
res.writeHead(200, {
  'Content-Type': 'application/pdf',
  'Content-disposition': 'attachment;filename=certificate.pdf',
  'Content-Length': buffer.length,
})
res.end(buffer)
......
......

Via above code we can written the binary data back to the client, which he can save it where-ever he wants to.

But the main problem is how do I get the buffer of the file being generated.

Please shed some light, happy coding :)


Solution

  • So, here is the solution of changing the buffered stream in to async await behaviour here is the generic solution that I made:

    DISCLAIMER: ITS NOT A GOOD PRACTICE FOR LARGER STREAMS, BUT CAN BE USED IN CASE YOU KNOW THE RESULTANT BUFFER WOULD BE FEW KB's

    const streamToBuffer = (streamObjectToRead) => {
       const chunks = []
       return new Promise((resolve, reject) => {
          streamObjectToRead
            .on('data', chunk => chunks.push(chunk))
            .on('end', () => resolve(Buffer.concat(chunks)))
            .on('error', err => reject(err))
       }) 
    }
    

    Now in order to use this, you can use like this:

    const someAsyncFunction = async () => {
    
        ......
        ...... //some code
    
        const stream = <FUNCTION THAT RETURNS A STREAM> // like wkhtmltopdf(compiledHtml, optionsObject)
    
        const fullBufferAfterReadingFullStream = await streamToBuffer(stream)
        ..... // do whatever you want to do with that buffer, eg: Writing to file, sending back to response in express framework.
    }
    

    Thanks & Happy Coding :)