Skip to content

Commit 9d7449d

Browse files
Replace ImageMagick with jimp
1 parent 4607ef5 commit 9d7449d

File tree

7 files changed

+611
-638
lines changed

7 files changed

+611
-638
lines changed

function/index.js

Lines changed: 19 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
1-
const gm = require('gm').subClass({ imageMagick: true });
2-
const AWS = require('aws-sdk');
1+
const S3 = require('./libs/s3');
2+
const Optimizer = require('./libs/optimize');
3+
const Utils = require('./libs/utils');
34

4-
const s3 = new AWS.S3();
5+
// Quality from 0 to 100
6+
const QUALITY = 60;
57

6-
exports.handler = async (event, context, cb) => {
7-
const validExtensions = ['jpg', 'jpeg', 'png'];
8+
// Where images are uploaded
9+
const ORIGIN = 'original/';
810

9-
const { bucket, object } = event.Records[0].s3;
11+
// Where optimized images will be saved
12+
const DESTINATION = 'thumbs/';
1013

11-
// Where images are uploaded
12-
const origin = 'original/';
14+
exports.handler = async (event, ctx, cb) => {
15+
const validExtensions = ['jpg', 'jpeg', 'png'];
1316

14-
// Where optimized images will be saved
15-
const dest = 'thumbs/';
17+
const { bucket, object } = event.Records[0].s3;
1618

17-
// Object key may have spaces or unicode non-ASCII characters. Remove prefix
18-
const fullFileName = decodeURIComponent(object.key.replace(/\+/g, ' '))
19-
.split('/').pop();
19+
const fullFileName = Utils.getFileName(object.key);
2020

2121
const [fileName, fileExt] = fullFileName.split('.');
2222

@@ -25,56 +25,24 @@ exports.handler = async (event, context, cb) => {
2525
}
2626

2727
// Download image from S3
28-
const s3Image = await s3.
29-
getObject({
30-
Bucket: bucket.name,
31-
Key: `${origin}${fullFileName}`
32-
})
33-
.promise();
34-
35-
function gmToBuffer(data) {
36-
return new Promise((resolve, reject) => {
37-
data.stream((err, stdout, stderr) => {
38-
if (err) { return reject(err) }
39-
const chunks = []
40-
stdout.on('data', (chunk) => { chunks.push(chunk) })
41-
stdout.once('end', () => { resolve(Buffer.concat(chunks)) })
42-
stderr.once('data', (data) => { reject(String(data)) })
43-
})
44-
})
45-
}
46-
47-
function getBuffer(body, size, quality) {
48-
const data = gm(body)
49-
.resize(size)
50-
.quality(quality);
51-
52-
return gmToBuffer(data);
53-
}
28+
const s3Image = await S3.download(bucket.name, `${ORIGIN}${fullFileName}`);
5429

5530
// use null to optimize image without resizing
5631
const sizes = [null, 1200, 640, 420];
5732

58-
// Uploades all images to S3
33+
// Uploades optimized images to S3
5934
const uploadPromises = sizes.map(async size => {
60-
// Optimize image with current size
61-
const imgBuffer = await getBuffer(s3Image.Body, size, 60);
62-
const key = size
63-
? `${dest}${fileName}_thumb_${size}.${fileExt}`
64-
: `${dest}${fileName}_original.${fileExt}`;
65-
66-
return s3.putObject({
67-
Bucket: bucket.name,
68-
Key: key,
69-
Body: imgBuffer,
70-
}).promise();
35+
const optimizedImage = await Optimizer.optimize(s3Image.Body, s3Image.ContentType, size, QUALITY);
36+
const objectKey = Utils.genKey(DESTINATION, fileName, size, fileExt);
37+
return S3.upload(bucket.name, objectKey, optimizedImage);
7138
});
7239

7340
await Promise.all(uploadPromises);
7441

7542
cb(null, 'finished');
7643
};
7744

45+
// Execute function if running in local
7846
if (process.env.LOCAL === 'true') {
7947
exports.handler(require('./event.json'), null, (err, res) => {
8048
console.log(res);

function/libs/optimize.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
const Halpert = require('jimp');
2+
3+
/**
4+
* Get an image as {buffer} and optimize with {size} and {quality}
5+
* @param { Buffer } buffer
6+
* @param { string } mime Mimetype of image (ex. image/jpeg)
7+
* @param { number } size Width of new image. null to keep original size
8+
* @param { number } quality 0-100 quality
9+
* @returns { Primise<Buffer> } Optimized image
10+
*/
11+
exports.optimize = async function optimize(buffer, mime, size, quality) {
12+
const data = await Halpert.read(buffer)
13+
if (size !== null) {
14+
data.resize(size, Halpert.AUTO);
15+
}
16+
data.quality(quality);
17+
18+
return data.getBufferAsync(mime);
19+
}

function/libs/resize.js

Whitespace-only changes.

function/libs/s3.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
const AWS = require('aws-sdk');
2+
const s3 = new AWS.S3();
3+
4+
/**
5+
* Download an object from S3
6+
* @param { string } Bucket
7+
* @param { string } Key
8+
*/
9+
exports.download = function download(Bucket, Key) {
10+
return s3.getObject({ Bucket, Key }).promise();
11+
}
12+
13+
/**
14+
* Upload an object to S3
15+
* @param { string } Bucket
16+
* @param { string } Key
17+
* @param { Buffer } Body
18+
*/
19+
exports.upload = function upload(Bucket, Key, Body) {
20+
return s3.putObject({ Bucket: Bucket, Key: Key, Body: Body }).promise();
21+
}

function/libs/utils.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* Generate an object key based on {size}
3+
* @param { string } dest Prefix of where images will be stored
4+
* @param { string } fileName
5+
* @param { number } size
6+
* @param { string } fileExt ex. png, jpg, jpeg (without dot)
7+
*/
8+
exports.genKey = function genKey(dest, fileName, size, fileExt) {
9+
return size
10+
? `${dest}${fileName}_thumb_${size}.${fileExt}`
11+
: `${dest}${fileName}_original.${fileExt}`;
12+
}
13+
14+
/**
15+
* Extract fileName from Object key by removing prefixes and cleaning non-ASCII characters
16+
* @param { string } key
17+
*/
18+
exports.getFileName = function getFileName(key) {
19+
// Object key may have spaces or unicode non-ASCII characters. Remove prefix
20+
return decodeURIComponent(key.replace(/\+/g, ' '))
21+
.split('/').pop();
22+
}

0 commit comments

Comments
 (0)