Skip to content

Instantly share code, notes, and snippets.

@ozasadnyy
Created November 1, 2017 10:44
Show Gist options
  • Save ozasadnyy/f1bc84eebe5335af4618955f77dc7d5c to your computer and use it in GitHub Desktop.
Save ozasadnyy/f1bc84eebe5335af4618955f77dc7d5c to your computer and use it in GitHub Desktop.
Optimize images with Firebase Storage and Functions on a fly
'use strict';
const functions = require('firebase-functions');
const gcs = require('@google-cloud/storage')();
const spawn = require('child-process-promise').spawn;
const mkdirp = require('mkdirp-promise');
const path = require('path');
const os = require('os');
const fs = require('fs');
exports.optimizeImages = functions.storage.object().onChange(({ data }) => {
// File and directory paths.
const filePath = data.name;
const tempLocalFile = path.join(os.tmpdir(), filePath);
const tempLocalDir = path.dirname(tempLocalFile);
// Exit if this is triggered on a file that is not an image.
if (!data.contentType.startsWith('image/')) {
console.log('This is not an image.');
return null;
}
// Exit if this is a move or deletion event.
if (data.resourceState === 'not_exists') {
console.log('This is a deletion event.');
return null;
}
// Exit if file exists but is not new and is only being triggered
// because of a metadata change.
if (data.resourceState === 'exists' && data.metageneration > 1) {
console.log('This is a metadata change event.');
return null;
}
// Cloud Storage files.
const bucket = gcs.bucket(data.bucket);
const file = bucket.file(filePath);
return file.getMetadata()
.then(([metadata]) => {
if (metadata.metadata && metadata.metadata.optimized) {
return Promise.reject('Image has been already optimized');
}
return Promise.resolve();
})
.then(() => mkdirp(tempLocalDir))
.then(() => file.download({ destination: tempLocalFile }))
.then(() => {
console.log('The file has been downloaded to', tempLocalFile);
// Generate a thumbnail using ImageMagick.
return spawn('convert', [tempLocalFile, '-strip', '-interlace', 'Plane', '-quality', '82', tempLocalFile]);
})
.then(() => {
console.log('Optimized image created at', tempLocalFile);
// Uploading the Optimized image.
return bucket.upload(tempLocalFile, {
destination: file,
metadata: {
metadata: {
optimized: true
}
}
});
})
.then(() => {
console.log('Optimized image uploaded to Storage at', file);
// Once the image has been uploaded delete the local files to free up disk space.
fs.unlinkSync(tempLocalFile);
// Get the Signed URLs for optimized image.
const config = {
action: 'read',
expires: '03-01-2500'
};
return file.getSignedUrl(config);
});
});
@mbove77
Copy link

mbove77 commented Aug 13, 2018

I have an error Cannot find module '@google-cloud/storage', but i install the module via npm:
npm install --save @google-cloud/storage

Function load error: Code in file index.js can't be loaded.
Did you list all required modules in the package.json dependencies?
Detailed stack trace: Error: Cannot find module '@google-cloud/storage'
at Function.Module._resolveFilename (module.js:476:15)
at Function.Module._load (module.js:424:25)
at Module.require (module.js:504:17)
at require (internal/module.js:20:19)
at Object. (/user_code/index.js:3:45)
at Module._compile (module.js:577:32)
at Object.Module._extensions..js (module.js:586:10)
at Module.load (module.js:494:32)
at tryModuleLoad (module.js:453:12)
at Function.Module._load (module.js:445:3)

@ianninirojas
Copy link

Hello I have an error. const gcs = require('@google-cloud/storage')(); TypeError: require(...) is not a function

@brianbentancourt
Copy link

Try this
const {Storage} = require('@google-cloud/storage'); const gcs = new Storage();

@androidovshchik
Copy link

androidovshchik commented May 10, 2019

I have wrote some different function
view here https://gist.github.com/androidovshchik/9e9c8299d393a28f74e17cf09d06d466

  • Written in typescript
  • Removed deprecated functions. E.g. functions.storage.object().onChange and data.resourceState are not existing now
  • Fixes metadata issue. Now when uploading old metadata is not erasing
  • Have better optimize arguments fo ImageMagick command
  • More safe execution
    And it really works for today

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment