Cloud Functions for Firebase - Convert PDF to Image

Cloud features for Firebase have this good example, where they create a thumbnail for each uploaded image. This is done using ImageMagick.

I tried to convert the sample to convert PDF files to images. This is what ImageMagick can do, but I can't get it to work with cloud functions for Firebase. I keep getting code 1:

ChildProcessError: `convert /tmp/cd9d0278-16b2-42be-aa3d-45b5adf89332.pdf[0] -density 200 /tmp/cd9d0278-16b2-42be-aa3d-45b5adf89332.pdf` failed with code 1 at ChildProcess.<anonymous> (/user_code/node_modules/child-process-promise/lib/index.js:132:23) at emitTwo (events.js:106:13) at ChildProcess.emit (events.js:191:7) at maybeClose (internal/child_process.js:877:16) at Socket.<anonymous> (internal/child_process.js:334:11) at emitOne (events.js:96:13) at Socket.emit (events.js:188:7) at Pipe._handle.close [as _onclose] (net.js:498:12) 

Of course, one possibility is that converting PDF files is simply not supported.

 const functions = require('firebase-functions'); const gcs = require('@google-cloud/storage')(); const spawn = require('child-process-promise').spawn; // [END import] // [START generateThumbnail] /** * When an image is uploaded in the Storage bucket We generate a thumbnail automatically using * ImageMagick. */ // [START generateThumbnailTrigger] exports.generateThumbnail = functions.storage.object().onChange(event => { // [END generateThumbnailTrigger] // [START eventAttributes] const object = event.data; // The Storage object. const fileBucket = object.bucket; // The Storage bucket that contains the file. const filePath = object.name; // File path in the bucket. const contentType = object.contentType; // File content type. const resourceState = object.resourceState; // The resourceState is 'exists' or 'not_exists' (for file/folder deletions). // [END eventAttributes] // [START stopConditions] // Exit if this is triggered on a file that is not an image. if (!contentType.startsWith('application/pdf')) { console.log('This is not a pdf.'); return; } // Get the file name. const fileName = filePath.split('/').pop(); // Exit if the image is already a thumbnail. if (fileName.startsWith('thumb_')) { console.log('Already a Thumbnail.'); return; } // Exit if this is a move or deletion event. if (resourceState === 'not_exists') { console.log('This is a deletion event.'); return; } // [END stopConditions] // [START thumbnailGeneration] // Download file from bucket. const bucket = gcs.bucket(fileBucket); const tempFilePath = `/tmp/${fileName}`; return bucket.file(filePath).download({ destination: tempFilePath }).then(() => { console.log('Pdf downloaded locally to', tempFilePath); // Generate a thumbnail of the first page using ImageMagick. return spawn('convert', [tempFilePath+'[0]' ,'-density', '200', tempFilePath]).then(() => { console.log('Thumbnail created at', tempFilePath); // Convert pdf extension to png const thumbFilePath = filePath.replace('.pdf', 'png'); // Uploading the thumbnail. return bucket.upload(tempFilePath, { destination: thumbFilePath }); }); }); // [END thumbnailGeneration] }); 
+5
source share
1 answer

Node modules can install their own code, which is located in the same directory as the source code of the cloud function. I found that some node libraries on github that do this for ghostscript, which is a very useful library for processing PDFs:

I put lambda-ghostscript in a subdirectory of my functions directory and then add node-gs as a dependency in my package file as follows:

 { "name": "functions", "dependencies": { "@google-cloud/storage": "^1.3.1", "child-process-promise": "^2.2.1", "firebase-admin": "~5.4.0", "firebase-functions": "^0.7.2", "gs": "https://github.com/sina-masnadi/node-gs/tarball/master" } } 

Then in my index.js file, I can simply require the node library to easily use ghostscript from JavaScript. Here's the full code for the cloud feature that uses the Google Cloud Storage trigger:

 const functions = require('firebase-functions'); const gcs = require('@google-cloud/storage')(); const spawn = require('child-process-promise').spawn; const path = require('path'); const os = require('os'); const fs = require('fs'); var gs = require('gs'); exports.makePNG = functions.storage.object().onChange(event => { // ignore delete events if (event.data.resourceState == 'not_exists') return false; const filePath = event.data.name; const fileDir = path.dirname(filePath); const fileName = path.basename(filePath); const tempFilePath = path.join(os.tmpdir(), fileName); if (fileName.endsWith('.png')) return false; if (!fileName.endsWith('.pdf')) return false; const newName = path.basename(filePath, '.pdf') + '.png'; const tempNewPath = path.join(os.tmpdir(), newName); // // Download file from bucket. const bucket = gcs.bucket(event.data.bucket); return bucket.file(filePath).download({ destination: tempFilePath }).then(() => { console.log('Image downloaded locally to', tempFilePath); return new Promise(function (resolve, reject) { gs() .batch() .nopause() .option('-r' + 50 * 2) .option('-dDownScaleFactor=2') .executablePath('lambda-ghostscript/bin/./gs') .device('png16m') .output(tempNewPath) .input(tempFilePath) .exec(function (err, stdout, stderr) { if (!err) { console.log('gs executed w/o error'); console.log('stdout',stdout); console.log('stderr',stderr); resolve(); } else { console.log('gs error:', err); reject(err); } }); }); }).then(() => { console.log('PNG created at', tempNewPath); // Uploading the thumbnail. return bucket.upload(tempNewPath, {destination: newName}); // Once the thumbnail has been uploaded delete the local file to free up disk space. }).then(() => { fs.unlinkSync(tempNewPath); fs.unlinkSync(tempFilePath); }).catch((err) => { console.log('exception:', err); return err; }); }); 

Here's a github project: https://github.com/ultrasaurus/ghostscript-cloud-function

Disclaimer: This is using compiled source code, and I tested experimentally what works for this case, so this is probably good. I did not consider specific compilation options and did not check whether they were correctly fixed for the environment of cloud functions.

+1
source

Source: https://habr.com/ru/post/1266392/


All Articles