0

I've started using Cloud Functions for Firebase to crop and resize multiple sizes when an image gets uploaded to my Firebase storage. I used the Firebase example code to get me started. Using the ImageMagick commands. However, I'm not getting crop to work.

It's my goal to get functionality, the same way Wordpress does. Upload image x. Make a fixed ratio crop. And resize to 3 sizes.

The code I have now resizes to a medium and large image and uploads them into my storage. Anyone have an approach on how to get the crop working?

This is my code so far:

'use strict';

const functions = require(`firebase-functions`);
const mkdirp = require(`mkdirp-promise`);
const gcs = require(`@google-cloud/storage`)();
const spawn = require(`child-process-promise`).spawn;
const LOCAL_TMP_FOLDER = `/tmp/`;

// Thumbnail prefix added to file names.
const THUMB_PREFIX_LARGE = `large_`;
const THUMB_PREFIX_MEDIUM = `medium_`;

/**
 * When an image is uploaded in the Storage bucket We generate a thumbnail automatically using
 * ImageMagick.
 */
exports.generateThumbnail = functions.storage.object().onChange(event => {
  const filePath = event.data.name;
  const filePathSplit = filePath.split(`/`);
  const fileName = filePathSplit.pop();
  const fileDir = filePathSplit.join(`/`) + (filePathSplit.length > 0 ? `/` : ``);

  const thumbFilePathLarge = `${fileDir}${THUMB_PREFIX_LARGE}${fileName}`;
  const thumbFilePathMedium = `${fileDir}${THUMB_PREFIX_MEDIUM}${fileName}`;

  const tempLocalDir = `${LOCAL_TMP_FOLDER}${fileDir}`;
  const tempLocalFile = `${tempLocalDir}${fileName}`;

  const tempLocalThumbFileLarge = `${LOCAL_TMP_FOLDER}${thumbFilePathLarge}`;
  const tempLocalThumbFileMedium = `${LOCAL_TMP_FOLDER}${thumbFilePathMedium}`;

  // Exit if this is triggered on a file that is not an image.
  if (!event.data.contentType.startsWith(`image/`)) {
    console.log(`This is not an image.`);
    return;
  }

  // Exit if the image is already a thumbnail.
  if (fileName.startsWith(THUMB_PREFIX_LARGE) || fileName.startsWith(THUMB_PREFIX_MEDIUM)) {
    console.log(`Already a Thumbnail.`);
    return;
  }

  // Exit if this is a move or deletion event.
  if (event.data.resourceState === `not_exists`) {
    console.log(`This is a deletion event.`);
    return;
  }

  // Create the temp directory where the storage file will be downloaded.
  return mkdirp(tempLocalDir).then(() => {
    // Download file from bucket.
    const bucket = gcs.bucket(event.data.bucket);
    return bucket.file(filePath).download({
      destination: tempLocalFile
    }).then(() => {
      console.log(`The file has been downloaded to`, tempLocalFile);
      // Generate a LARGE thumbnail using ImageMagick.
      return spawn(`convert`, [tempLocalFile, `-thumbnail`, `1200x800`, tempLocalThumbFileLarge]).then(() => {
        console.log(`Thumbnail created at`, tempLocalThumbFileLarge);
        // Uploading the large Thumbnail.
        return bucket.upload(tempLocalThumbFileLarge, {
          destination: thumbFilePathLarge
        }).then(() => {
          console.log(`Thumbnail uploaded to Storage at`, thumbFilePathLarge);
        }).then(() => {
          console.log(`The file has been downloaded to`, tempLocalFile);
          // Generate a MEDIUM thumbnail using ImageMagick.
          return spawn(`convert`, [tempLocalFile, `-thumbnail`, `600x400`, tempLocalThumbFileMedium]).then(() => {
            console.log(`Thumbnail created at`, tempLocalThumbFileMedium);
            // Uploading the medium Thumbnail.
            return bucket.upload(tempLocalThumbFileMedium, {
              destination: thumbFilePathMedium
            }).then(() => {
              console.log(`Thumbnail uploaded to Storage at`, thumbFilePathMedium);
            });
          });
        });
      });
    });
  });
});
Doug Stevenson
  • 297,357
  • 32
  • 422
  • 441

3 Answers3

2

I fixed it doing this.

return mkdirp(tempLocalDir).then(() => {
    const bucket = gcs.bucket(event.data.bucket);
    return bucket.file(filePath).download({
      destination: tempLocalFile
    }).then(() => {
      return spawn(`convert`, [`-define`, `jpeg:size=1200x800`, tempLocalFile, `-thumbnail`, `1200x800^`, `-gravity`, `center`, `-extent`, `1200x800`, tempLocalThumbFileLarge]).then(() => {
        return bucket.upload(tempLocalThumbFileLarge, {
          destination: thumbFilePathLarge
        }).then(() => {
          return spawn(`convert`, [`-define`, `jpeg:size=600x400`, tempLocalFile, `-thumbnail`, `600x400^`, `-gravity`, `center`, `-extent`, `600x400`, tempLocalThumbFileMedium]).then(() => {
            return bucket.upload(tempLocalThumbFileMedium, {
              destination: thumbFilePathMedium
            });
          });
        });
      });
    });
  });
0

You should be able to use -crop the same way you are using -thumbnail at the moment

http://www.imagemagick.org/Usage/crop/

Clinton
  • 973
  • 6
  • 14
0

This question is old and I don't know if this option existed when asked, but no you can resize and crop the image to the desired size in one operation.

Visit this doc Imagemagick Thumbnails and read the section "Cut the Thumbnail to Fit" for more information. For example:

convert -define jpeg:size=200x200 hatching_orig.jpg -thumbnail 100x100^ -gravity center -extent 100x100 cut_to_fit.gif

With your code you could call it like this:

await spawn('convert', [
  tempLocalFile,
  '-thumbnail', `1200x800^`,
  '-gravity', 'center',
  '-extent', `1200x800`,
  tempLocalThumbFileLarge
]);
Gonzalo
  • 1,781
  • 15
  • 29