Skip to content

Commit

Permalink
Update s3-resizer 4.0
Browse files Browse the repository at this point in the history
Technically, the interface is still compatible with version 3.x (`100x100_max/my-image.jpg` - the path is still the same), but this update brings major changes including using new import/export syntax, so the decision was to jump to a new version.

Here is what's new:
- Update the Sharp library: [v0.23.3](https://www.npmjs.com/package/sharp/v/0.23.3) => [v0.32.0](https://www.npmjs.com/package/sharp/v/0.32.0)
- Update AWS SDK: [aws-sdk v2.36.0](https://www.npmjs.com/package/aws-sdk/v/2.36.0) => [@aws-sdk/client-s3 v3.304.0](https://www.npmjs.com/package/@aws-sdk/client-s3/v/3.304.0). The former api contained the whole sdk with 99% unused code whereas the new one contains functions specific to S3.
- Buffers were replaced by streams. So now it's almost impossible to go out-of-memory if an image is too large (though it is still highly recommended to keep memory big +512mb, you can do that in Configuration->Edit)
- CommonJS => ES Module. And hence `require/exports` => `import/export`. They promise to be faster as well.
- Reduce zip size containing compiled node_modules: 30mb => 10mb.
- If `?path=` is not set, return explaining message and `statusCode: 400`. Previously it was `Internal Server Error` with `statusCode: 500`.

🎉 Compiled and tested for NodeJS 18.x 🎉
```bash
npm i --arch=x64 --platform=linux

npm i --arch=x64 --platform=linux --only=prod
```

Use the latter only if you know what you are doing! It does not contain awk libraries in node_modules, so the latest version of the sdk will be used. You can configure auto update of built-in sdk in lambda _Runtime settings -> Edit runtime management configuration -> Update runtime version_.
  • Loading branch information
sagidM committed Apr 1, 2023
1 parent 1015d1b commit e38850f
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 40 deletions.
82 changes: 46 additions & 36 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
'use strict'
import {S3Client, GetObjectCommand} from "@aws-sdk/client-s3";
import {Upload} from '@aws-sdk/lib-storage';
import Sharp from 'sharp';


const AWS = require('aws-sdk');
const S3 = new AWS.S3({signatureVersion: 'v4'});
const Sharp = require('sharp');
const PathPattern = /(.*\/)?(.*)\/(.*)/;

// parameters
Expand All @@ -12,10 +10,18 @@ const WHITELIST = process.env.WHITELIST
? Object.freeze(process.env.WHITELIST.split(' '))
: null;

const s3Client = new S3Client();

export const handler = async (event) => {
const path = event.queryStringParameters?.path;
if (!path) {
return errorResponse('Parameter "?path=" is empty or missing');
}

exports.handler = async (event) => {
const path = event.queryStringParameters.path;
const parts = PathPattern.exec(path);
if (!parts || !parts[2] || !parts[3]) {
return errorResponse('Parameter "?path=" should look like: path/150x150_max/image.jpg');
}
const dir = parts[1] || '';
const resizeOption = parts[2]; // e.g. "150x150_max"
const sizeAndAction = resizeOption.split('_');
Expand All @@ -26,27 +32,22 @@ exports.handler = async (event) => {

// Whitelist validation.
if (WHITELIST && !WHITELIST.includes(resizeOption)) {
return {
statusCode: 400,
body: `WHITELIST is set but does not contain the size parameter "${resizeOption}"`,
headers: {"Content-Type": "text/plain"}
};
return errorResponse(`WHITELIST is set but does not contain the size parameter "${resizeOption}"`, 403);
}

// Action validation.
if (action && action !== 'max' && action !== 'min') {
return {
statusCode: 400,
body: `Unknown func parameter "${action}"\n` +
'For query ".../150x150_func", "_func" must be either empty, "_min" or "_max"',
headers: {"Content-Type": "text/plain"}
};
return errorResponse(
`Unknown func parameter "${action}"\n` +
'For query ".../150x150_func", "_func" must be either empty, "_min" or "_max"'
);
}

try {
const data = await S3
.getObject({Bucket: BUCKET, Key: dir + filename})
.promise();
const originImage = await s3Client.send(new GetObjectCommand({
Bucket: BUCKET,
Key: dir + filename,
}));

const width = sizes[0] === 'AUTO' ? null : parseInt(sizes[0]);
const height = sizes[1] === 'AUTO' ? null : parseInt(sizes[1]);
Expand All @@ -62,28 +63,37 @@ exports.handler = async (event) => {
fit = 'cover';
break;
}
const result = await Sharp(data.Body, {failOnError: false})
const sharp = Sharp({failOn: 'none'})
.resize(width, height, {withoutEnlargement: true, fit})
.rotate()
.toBuffer();
.rotate();

await S3.putObject({
Body: result,
Bucket: BUCKET,
ContentType: data.ContentType,
Key: path,
CacheControl: 'public, max-age=86400'
}).promise();
// This does not work: await s3Client.send(new PutObjectCommand({params})
// Solution: https://github.com/aws/aws-sdk-js-v3/issues/1920#issuecomment-761298908
const upload = new Upload({
client: s3Client,
params: {
Bucket: BUCKET,
Key: path,
Body: originImage.Body.pipe(sharp),
ContentType: originImage.ContentType,
CacheControl: 'public, max-age=86400'
},
});
await upload.done();

return {
statusCode: 301,
headers: {"Location" : `${URL}/${path}`}
};
} catch (e) {
return {
statusCode: e.statusCode || 400,
body: 'Exception: ' + e.message,
headers: {"Content-Type": "text/plain"}
};
return errorResponse('Exception: ' + e.message, e.statusCode || 400);
}
}

function errorResponse(body, statusCode = 400) {
return {
statusCode,
body,
headers: {"Content-Type": "text/plain"}
}
}
10 changes: 6 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
{
"name": "s3-resizer",
"version": "3.0.1",
"version": "4.0.0",
"dependencies": {
"sharp": "^0.23.3"
"sharp": "^0.32.0"
},
"devDependencies": {
"aws-sdk": "^2.36.0"
}
"@aws-sdk/client-s3": "^3.304.0",
"@aws-sdk/lib-storage": "^3.304.0"
},
"type": "module"
}

0 comments on commit e38850f

Please sign in to comment.