Skip to content

Instantly share code, notes, and snippets.

@kageurufu
Last active November 25, 2021 12:44
Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save kageurufu/68667fcb6d3a08078616 to your computer and use it in GitHub Desktop.
Save kageurufu/68667fcb6d3a08078616 to your computer and use it in GitHub Desktop.
AWS Lambda Thumbnailer
var async = require("async");
var AWS = require("aws-sdk");
var gm = require("gm").subClass({imageMagick: true});
var fs = require("fs");
var mktemp = require("mktemp");
var THUMB_KEY_PREFIX = "thumbnails/",
THUMB_WIDTH = 150,
THUMB_HEIGHT = 150,
ALLOWED_FILETYPES = ['png', 'jpg', 'jpeg', 'bmp', 'tiff', 'pdf', 'gif'];
var utils = {
decodeKey: function(key) {
return decodeURIComponent(key).replace(/\+/g, ' ');
}
};
var s3 = new AWS.S3();
exports.handler = function(event, context) {
var bucket = event.Records[0].s3.bucket.name,
srcKey = utils.decodeKey(event.Records[0].s3.object.key),
dstKey = THUMB_KEY_PREFIX + srcKey.replace(/\.\w+$/, ".png"),
fileType = srcKey.match(/\.\w+$/);
if(srcKey.indexOf(THUMB_KEY_PREFIX) === 0) {
return;
}
if (fileType === null) {
console.error("Invalid filetype found for key: " + srcKey);
return;
}
fileType = fileType[0].substr(1);
if (ALLOWED_FILETYPES.indexOf(fileType) === -1) {
console.error("Filetype " + fileType + " not valid for thumbnail, exiting");
return;
}
async.waterfall([
function download(next) {
//Download the image from S3
s3.getObject({
Bucket: bucket,
Key: srcKey
}, next);
},
function createThumbnail(response, next) {
var temp_file, image;
if(fileType === "pdf") {
temp_file = mktemp.createFileSync("/tmp/XXXXXXXXXX.pdf")
fs.writeFileSync(temp_file, response.Body);
image = gm(temp_file + "[0]");
} else if (fileType === 'gif') {
temp_file = mktemp.createFileSync("/tmp/XXXXXXXXXX.gif")
fs.writeFileSync(temp_file, response.Body);
image = gm(temp_file + "[0]");
} else {
image = gm(response.Body);
}
image.size(function(err, size) {
/*
* scalingFactor should be calculated to fit either the width or the height
* within 150x150 optimally, keeping the aspect ratio. Additionally, if the image
* is smaller than 150px in both dimensions, keep the original image size and just
* convert to png for the thumbnail's display
*/
var scalingFactor = Math.min(1, THUMB_WIDTH / size.width, THUMB_HEIGHT / size.height),
width = scalingFactor * size.width,
height = scalingFactor * size.height;
this.resize(width, height)
.toBuffer("png", function(err, buffer) {
if(temp_file) {
fs.unlinkSync(temp_file);
}
if (err) {
next(err);
} else {
next(null, response.contentType, buffer);
}
});
});
},
function uploadThumbnail(contentType, data, next) {
s3.putObject({
Bucket: bucket,
Key: dstKey,
Body: data,
ContentType: "image/png",
ACL: 'public-read',
Metadata: {
thumbnail: 'TRUE'
}
}, next);
}
],
function(err) {
if (err) {
console.error(
"Unable to generate thumbnail for '" + bucket + "/" + srcKey + "'" +
" due to error: " + err
);
} else {
console.log("Created thumbnail for '" + bucket + "/" + srcKey + "'");
}
context.done();
});
};
@niczak
Copy link

niczak commented Mar 20, 2018

Is there by chance an updated version of this?

@kageurufu
Copy link
Author

What does it not do that you need? We are still using this in production

https://github.com/DisruptiveLabs/lambda-thumbnailer

@jp928
Copy link

jp928 commented Jun 1, 2019

@kageurufu
Thanks for sharing your code. I am facing the problem the deploy Graphicmagick or ImageMagick. Have you compile the binary and upload them with your code as mentioned in https://gist.github.com/bensie/56f51bc33d4a55e2fc9a?

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