Search code examples
parse-platformparse-cloud-code

How to write Parse Cloud Code to make Thumbnails


Assume I have a Table

---------Image-------------

imageFile (File)   |    thumbnail (File)  | Post (a pointer to Post)

Any idea how to write a cloud code to take a smaller version of that image in another column?

For example, If a user uploaded a (2000x3500 px) image, Parse will save it in imageFile column, and save its thumbnail in the other column

thanks


Solution

  • Here's the current solution I'm using (Original script was published by Parse.com some time ago and deleted after the ownership transfer to the community):

    Usage example in main.js:

    var resizeImageKey = require('cloud/resize-image-key.js'),
        THUMBNAIL_SIZE = 100,
        Image = require("parse-image");
    
    
    Parse.Cloud.afterSave("TableName", function(request) {
        return resizeImageKey({
            object: request.object, // ParseObject in which the thumbnail will be saved
            url: objectImage.url(), // Full size image URL
            toKey: "thumbnail", // thumbnail's attribute key
            width: THUMBNAIL_SIZE, // width
            crop: false // resize or crop ?
        });
    });
    

    resize-image-key.js (async-await)

    const Image = require("parse-image");
    
    /*
      Original: https://github.com/ParsePlatform/Anyimg/blob/master/parse/cloud/resize-image-key.js
      Resizes an image from one Parse Object key containing
      a Parse File to a file object at a new key with a target width.
      If the image is smaller than the target width, then it is simply
      copied unaltered.
    
      object: Parse Object
      url: URL of the Parse File
      toKey: Key to contain the target Parse File
      width: Target width
      crop: Center crop the square
    */
    module.exports = function(options) {
        let format, originalHeight, originalWidth, newHeight, newWidth;
    
        // First get the image data
        const response = await Parse.Cloud.httpRequest({
            //url: options.object.get(options.fromKey).url()
            url: options.url
        })
    
        let image = new Image();
        await image.setData(response.buffer);
    
        // set some metadata that will be on the object
        format = image.format();
        originalHeight = image.height();
        originalWidth = image.width();
        if (image.width() <= options.width) {
            // No need to resize
            // Remove from code
        } else {
            var newWidth = options.width;
            var newHeight = options.width * image.height() / image.width();
    
            // If we're cropping to a square, then we need to adjust height and
            // width so that the greater length of the two fits the square
            if (options.crop && (newWidth > newHeight)) {
                var newHeight = options.width;
                var newWidth = newHeight * image.width() / image.height();
            }
    
            // resize down to normal width size
            image = await image.scale({
                width: newWidth,
                height: newHeight
            });
        }
    
        // crop ?
        if (options.crop) {
            let left = 0;
            let top = 0;
    
            // Center crop
            if (image.width() > image.height()) {
                left = (image.width() - image.height()) / 2;
            } else {
                top = (image.height() - image.width()) / 2;
            }
    
            image = await image.crop({
                left: left,
                top: top,
                width: options.width,
                height: options.width
            });
        }
    
        newHeight = image.height();
        newWidth = image.width();
        // Get the image data in a Buffer.
        const buffer = await image.data();
    
        // Save the image into a new file.
        const base64 = buffer.toString("base64");
        //var scaled = new Parse.File("thumbnail_" + options.object.get("name") + "." + format, {
        const scaled = new Parse.File("thumbnail." + format, {
            base64: base64
        });
        const savedImage = await scaled.save();
    
        // Set metadata on the image object
        options.object.set(options.toKey, savedImage);
        //return options.object.save();
        options.object.set("thumbnail_width", newWidth);
        options.object.set("thumbnail_height", newHeight);
    };
    

    resize-image-key.js (vanilla JS)

    var Image = require("parse-image");
    
    /*
      Original: https://github.com/ParsePlatform/Anyimg/blob/master/parse/cloud/resize-image-key.js
      Resizes an image from one Parse Object key containing
      a Parse File to a file object at a new key with a target width.
      If the image is smaller than the target width, then it is simply
      copied unaltered.
    
      object: Parse Object
      url: URL of the Parse File
      toKey: Key to contain the target Parse File
      width: Target width
      crop: Center crop the square
    */
    module.exports = function(options) {
        var format, originalHeight, originalWidth, newHeight, newWidth;
    
        // First get the image data
        return Parse.Cloud.httpRequest({
            //url: options.object.get(options.fromKey).url()
            url: options.url
        }).then(function(response) {
            var image = new Image();
            return image.setData(response.buffer);
        }).then(function(image) {
            // set some metadata that will be on the object
            format = image.format();
            originalHeight = image.height();
            originalWidth = image.width();
    
            if (image.width() <= options.width) {
                // No need to resize
                return new Parse.Promise.as(image);
            } else {
                var newWidth = options.width;
                var newHeight = options.width * image.height() / image.width();
    
                // If we're cropping to a square, then we need to adjust height and
                // width so that the greater length of the two fits the square
                if (options.crop && (newWidth > newHeight)) {
                    var newHeight = options.width;
                    var newWidth = newHeight * image.width() / image.height();
                }
    
                // resize down to normal width size
                return image.scale({
                    width: newWidth,
                    height: newHeight
                });
            }
        }).then(function(image) {
            if (options.crop) {
                var left = 0;
                var top = 0;
    
                // Center crop
                if (image.width() > image.height()) {
                    var left = (image.width() - image.height()) / 2;
                } else {
                    var top = (image.height() - image.width()) / 2;
                }
    
                return image.crop({
                    left: left,
                    top: top,
                    width: options.width,
                    height: options.width
                });
            } else {
                return Parse.Promise.as(image);
            }
        }).then(function(image) {
            newHeight = image.height();
            newWidth = image.width();
            // Get the image data in a Buffer.
            return image.data();
        }).then(function(buffer) {
            // Save the image into a new file.
            var base64 = buffer.toString("base64");
            //var scaled = new Parse.File("thumbnail_" + options.object.get("name") + "." + format, {
            var scaled = new Parse.File("thumbnail." + format, {
                base64: base64
            });
            return scaled.save();
        }).then(function(image) {
            // Set metadata on the image object
            options.object.set(options.toKey, image);
            //return options.object.save();
            options.object.set("thumbnail_width", newWidth);
            options.object.set("thumbnail_height", newHeight);
        });
    };
    

    Update: Found a clone of the original script and archived it just in case: https://web.archive.org/web/20190107225015/https://github.com/eknight7/Anyimg/blob/master/parse/cloud/resize-image-key.js