While cleaning the cloud functions of one Firebase project, i found a set of routines to save a pdf file to Google Cloud Storage. I'm removing them because i moved the pdf generation to client side.
Posting here in hope it can be useful for someone or my future self.
Use case
The pdf is generated dynamically with pdfkit in a firebase cloud function and cached in Google Cloud Storage bucket.
Generate the Data URL
The first function is to generate a Data URL from a pdfkit document
async function generatePdfDataUrl(doc) {
return new Promise((resolve, reject) => {
const chunks = []
doc.on('data', (chunk) => {
chunks.push(chunk)
})
doc.on('end', () => {
const result = Buffer.concat(chunks)
resolve('data:application/pdf;base64,' + result.toString('base64'))
})
doc.on('error', (error) => {
reject(error)
})
doc.end()
})
}
It receives a pdf document and wraps the node stream events in a promise. Stores the file chunks and, at the end, concatenate them after converting to base64.
Save to Cloud Storage
const isEmulator = process.env.FUNCTIONS_EMULATOR
const baseFileURL = 'https://storage.googleapis.com/XXX.appspot.com/'
async function storePdfDoc(storage, path, doc) {
if (isEmulator) {
const fileUrl = await generatePdfDataUrl(doc)
return { fileUrl }
}
// toLocalDate converts cloud date (UTC) to local GMT-3 in my case
const reportDate = toLocalDate(new Date())
const formattedDate = format(reportDate, 'dd-MM-yy-kk-mm')
const bucket = storage.bucket()
const fileName = slugify(`${path}-${formattedDate}.pdf`)
const file = bucket.file(fileName)
const bucketFileStream = file.createWriteStream()
return new Promise((resolve, reject) => {
bucketFileStream.on('finish', () => {
const link = `${baseFileURL}${fileName}`
resolve({ fileUrl: link })
})
bucketFileStream.on('error', function bucketError(err) {
reject(err)
})
doc.pipe(bucketFileStream)
doc.end()
})
}
The first step is to check if is running in firebase emulator and return the Data URL instead of storing the file
At the time i wrote this function the firebase Storage emulator was not available
This function also wrap node stream functions in a promise. This time, we create a file in the bucket using bucket.file
and the corresponding writable stream, than pipe the pdfkit document to it.
The catch here is to resolve the promise in the destination stream 'finish' event instead of document 'end' event. This way we ensure the file is fully created / uploaded on the cloud.
Top comments (0)