diff --git a/index.js b/index.js index ef7d22a..594020d 100755 --- a/index.js +++ b/index.js @@ -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 @@ -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('_'); @@ -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]); @@ -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"} } } diff --git a/package.json b/package.json index 078a137..374869c 100755 --- a/package.json +++ b/package.json @@ -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" }