I followed the Firebase functions tutorials from the firecasts: https://www.youtube.com/watch?v=pDLpEn3PbmE&t=338s to use firebase to create a thumbnail when I upload an image.

This all works well but when I upload an image that has been taken with an iPhone it's rotated (vertical image is saved horizontally). So I did some research about that and I came across the -auto-orient parameter from ImageMagick (http://magick.imagemagick.org/script/command-line-options.php#auto-orient)

But I'm not sure how I add this parameter to the spawn function to take this parameter into account

My working code (just the part of it that is relevant) without the -auto-orient

...
return bucket.file(filePath).download({
        destination: tempFilePath
    })
    .then(() => {
        console.log('Image downloaded locally to', tempFilePath);
        return spawn('convert', [tempFilePath, '-thumbnail', '200x200>', tempFilePath]);
    })
    .then(() => {
        console.log('Thumbnail created!');
        const thumbFilePath = filePath.replace(/(\/)?([^\/]*)$/, '$1thumb_$2');
        console.log(`thumbFilePath: ${thumbFilePath}`);
        return bucket.upload(tempFilePath, {
            destination: thumbFilePath
        });
    }) 
...

the code that I tried with the -auto-orient parameter

...
return bucket.file(filePath).download({
        destination: tempFilePath
    })
    .then(() => {
        console.log('Image downloaded locally to', tempFilePath);
        return spawn('convert', ['-auto-orient', tempFilePath, '-thumbnail', '200x200>', tempFilePath]);
    })
    .then(() => {
        console.log('Thumbnail created!');
        const thumbFilePath = filePath.replace(/(\/)?([^\/]*)$/, '$1thumb_$2');
        console.log(`thumbFilePath: ${thumbFilePath}`);
        return bucket.upload(tempFilePath, {
            destination: thumbFilePath
        });
    })
...

But when I deploy this to firebase and I try to upload an image I get the following error message which doesn't give me a lot of information about why it is not working

Function execution took 6227 ms, finished with status: 'connection error'

any ideas?


I was also experiencing the "Connection Error" message inside GCF. I did 3 things to fix it, though I am not entirely sure if only 1 was the cause or if it was all 3. They were:

  • Unresolved Promises
  • Lack of using the callback() function provided by GCF
  • Using require('child-process-promise').exec instead of require('child-process-promise').spawn

Here's my code, which has been running 2x/second for 12 hours now without encountering the "Connection Error" message.

const Storage = require('@google-cloud/storage');
const exec    = require('child-process-promise').exec;
const uuidv1  = require('uuid/v1');
const _       = require('lodash');

exports.processFile = (event, callback) => {
    const file         = event.data;

    if(file.contentType.indexOf('image/') !== 0) {
        console.log(file.name + ' is not an image');
        callback();
    } else if(!_.isUndefined(file.metadata) && !_.isUndefined(file.metadata['x-processed'])) {
        console.log(file.name + ' was already processed');
        callback();
    } else if(file.resourceState === 'not_exists') {
        console.log('This is a deletion event.');
        callback();
    } else if (file.resourceState === 'exists' && file.metageneration > 1) {
        console.log('This is a metadata change event.');
        callback();
    } else {
        const storage       = new Storage();
        const bucket        = storage.bucket(file.bucket);
        const parts         = file.name.split('.');
        const tempFilePath  = '/tmp/' + uuidv1() + '.' + _.last(parts);
        const tempFinalPath = '/tmp/' + uuidv1() + '.' + _.last(parts);

        console.log('Processing file: ' + file.name);

        return bucket.file(file.name).download({
            destination: tempFilePath
        })
        .then(() => {
            console.log('Image downloaded locally to ', tempFilePath);

            return exec(`convert -auto-orient "${tempFilePath}" "${tempFinalPath}"`)
        })
        .then(() => {
            console.log('uploading modified file to ' + file.name);

            return bucket.upload(tempFinalPath, {
                destination: file.name,
                contentType: file.contentType,
                metadata: {
                    metadata: {
                        "x-processed": "yes"
                    }
                }
            })
        })
        .then(() => {
            console.log('file uploaded successfully to ' + file.name);
            callback()
        })
        .catch((err) => {
            callback(err);
        })
    }
}