Create square thumbnail in firebase cloud functions
Asked Answered
T

1

6

I want to create a square thumbnail when a image was uploaded to cloud storage in firebase. I just want the center of the image cropped out in square, and have it resize it to 150px x 150px. Something like this:

When this is uploaded enter image description here

I want to get this

enter image description here

and when this is uploaded

enter image description here

I want to get this

enter image description here

I wanted to do this in Firebase, but they don't just use Imagemagick, but instead uses spawn, and I could not figure out how these two go together.

For the code, I just pretty much have the code copied and pasted from here: https://github.com/firebase/functions-samples/blob/master/quickstarts/thumbnails/functions/index.js

I could not even get the dimensions of the image successfully, so how can I get the dimension first, and achieve what I want to achieve?

/**
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for t`he specific language governing permissions and
 * limitations under the License.
 */
'use strict';

// [START import]
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');
// [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).
  const metageneration = object.metageneration; // Number of times metadata has been generated. New objects have a value of 1.
  // [END eventAttributes]

  // [START stopConditions]
  // Exit if this is triggered on a file that is not an image.
  if (!contentType.startsWith('image/')) {
    console.log('This is not an image.');
    return;
  }

  // Get the file name.
  const fileName = path.basename(filePath);
  // 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;
  }

  // Exit if file exists but is not new and is only being triggered
  // because of a metadata change.
  if (resourceState === 'exists' && metageneration > 1) {
    console.log('This is a metadata change event.');
    return;
  }
  // [END stopConditions]

  // [START thumbnailGeneration]
  // Download file from bucket.
  const bucket = gcs.bucket(fileBucket);
  const tempFilePath = path.join(os.tmpdir(), fileName);
  const metadata = { contentType: contentType };
  return bucket.file(filePath).download({
    destination: tempFilePath
  }).then(() => {
    console.log('Image downloaded locally to', tempFilePath);
    // Generate a thumbnail using ImageMagick.
    return spawn('convert', [tempFilePath, '-thumbnail', '200x200>', tempFilePath]);
  }).then(() => {
    console.log('Thumbnail created at', tempFilePath);
    // We add a 'thumb_' prefix to thumbnails file name. That's where we'll upload the thumbnail.
    const thumbFileName = `thumb_${fileName}`;
    const thumbFilePath = path.join(path.dirname(filePath), thumbFileName);
    // Uploading the thumbnail.
    return bucket.upload(tempFilePath, { destination: thumbFilePath, metadata: metadata });
  // Once the thumbnail has been uploaded delete the local file to free up disk space.
  }).then(() => fs.unlinkSync(tempFilePath));
  // [END thumbnailGeneration]
});
// [END generateThumbnail]
Thermosetting answered 26/11, 2017 at 20:54 Comment(0)
G
4

This is the imagemagick code line:

return spawn('convert', [tempFilePath, '-thumbnail', '200x200>', tempFilePath]);

It is just creating a thumbnail and the > at the end of the dimensions tells it to only shrink larger files but keep the aspect ratio. You want to add a -gravity center -crop 200x200+0+0 before the temp file path. I have no idea if it is as easy as , '-gravity', 'center', '-crop', '200x200+0+0'

Also you could have a problem with aspect ratios but you can try it on a portrate and landscape images and see what you get.

Gavin answered 26/11, 2017 at 21:3 Comment(3)
Okay, I changed return spawn('convert', [tempFilePath, '-thumbnail', '200x200>', tempFilePath]); to return spawn('convert', [tempFilePath, '-thumbnail', '200x200>', '-gravity', 'center', '-crop', '200x200+0+0', tempFilePath]); , but nothing happens. So, '-thumbnail', '200x200>' makes longer side of the image 200px, right? What I need is a command that makes the shorter side of the image 200px. For example, if I upload an image that is 3000px x 2000px, I want to first resize it to 300px x 200px and then perform the '-crop', '200x200+0+0'.Thermosetting
Try changing the > to a ^ imagemagick.org/script/command-line-processing.php#geometryGavin
This worked!! Thank you so much! You have save my day!Thermosetting

© 2022 - 2024 — McMap. All rights reserved.