Skip to content

Commit

Permalink
Updated to Version 4.2
Browse files Browse the repository at this point in the history
  • Loading branch information
georgebearden committed Feb 6, 2020
1 parent a7ee268 commit 19cbc3c
Show file tree
Hide file tree
Showing 14 changed files with 633 additions and 149 deletions.
31 changes: 30 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,34 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [4.2] - 2020-02-06
### Added
- Honor outputFormat Parameter from the pull request [#117](https://github.com/awslabs/serverless-image-handler/pull/117)
- Support serving images under s3 subdirectories, Fix to make /fit-in/ work; Fix for VipsJpeg: Invalid SOS error plus several other critical fixes from the pull request [#130](https://github.com/awslabs/serverless-image-handler/pull/130)
- Allow regex in SOURCE_BUCKETS for environment variable from the pull request [#138](https://github.com/awslabs/serverless-image-handler/pull/138)
- Fix build script on other platforms from the pull request [#139](https://github.com/awslabs/serverless-image-handler/pull/139)
- Add Cache-Control response header from the pull request [#151](https://github.com/awslabs/serverless-image-handler/pull/151)
- Add AUTO_WEBP option to automatically serve WebP if the client supports it from the pull request [#152](https://github.com/awslabs/serverless-image-handler/pull/152)
- Use HTTP 404 & forward Cache-Control, Content-Type, Expires, and Last-Modified headers from S3 from the pull request [#158](https://github.com/awslabs/serverless-image-handler/pull/158)
- fix: DeprecationWarning: Buffer() is deprecated from the pull request [#174](https://github.com/awslabs/serverless-image-handler/pull/174)
- Add hex color support for Thumbor ```filters:background_color``` and ```filters:fill``` [#154](https://github.com/awslabs/serverless-image-handler/issues/154)
- Add format and watermark support for Thumbor [#109](https://github.com/awslabs/serverless-image-handler/issues/109), [#131](https://github.com/awslabs/serverless-image-handler/issues/131), [#109](https://github.com/awslabs/serverless-image-handler/issues/142)
* __Note that__ duplicated features has been merged gracefully.

### Changed
- sharp base version (from 0.23.3 to 0.23.4)
- Image handler Amazon CloudFront distribution ```DefaultCacheBehavior.ForwaredValues.Header``` to ```["Origin", "Accept"]``` for webp
- Image resize process change for ```filters:no_upscale()``` handling by ```withoutEnlargement``` edit key [#144](https://github.com/awslabs/serverless-image-handler/issues/144)

### Fixed
- Add and fix Cache-control, Content-Type, Expires, and Last-Modified headers to response: [#103](https://github.com/awslabs/serverless-image-handler/issues/103), [#107](https://github.com/awslabs/serverless-image-handler/issues/107), [#120](https://github.com/awslabs/serverless-image-handler/issues/120)
- Fix Amazon S3 bucket subfolder issue: [#106](https://github.com/awslabs/serverless-image-handler/issues/106), [#112](https://github.com/awslabs/serverless-image-handler/issues/112), [#119](https://github.com/awslabs/serverless-image-handler/issues/119), [#123](https://github.com/awslabs/serverless-image-handler/issues/123), [#167](https://github.com/awslabs/serverless-image-handler/issues/167), [#175](https://github.com/awslabs/serverless-image-handler/issues/175)
- Fix HTTP status code for missing images from 500 to 404: [#159](https://github.com/awslabs/serverless-image-handler/issues/159)
- Fix European character in filename issue: [#149](https://github.com/awslabs/serverless-image-handler/issues/149)
- Fix image scaling issue for filename containing 'x' character: [#163](https://github.com/awslabs/serverless-image-handler/issues/163), [#176](https://github.com/awslabs/serverless-image-handler/issues/176)
- Fix regular expression issue: [#114](https://github.com/awslabs/serverless-image-handler/issues/114), [#121](https://github.com/awslabs/serverless-image-handler/issues/121), [#125](https://github.com/awslabs/serverless-image-handler/issues/125)
- Fix not working quality parameter: [#129](https://github.com/awslabs/serverless-image-handler/issues/129)

## [4.1] - 2019-12-31
### Added
- CHANGELOG file
Expand All @@ -15,5 +43,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Image handler function to use Composite API (https://sharp.pixelplumbing.com/en/stable/api-composite/)
- License to Apache-2.0

# Removed
### Removed
- Reference to deprecated sharp function (overlayWith)
- Capability to resize images proportionally if width or height is set to 0 (sharp v0.23.1 and later check that the width and height - if present - are positive integers)
2 changes: 1 addition & 1 deletion LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.

Copyright 2019 - 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand Down
2 changes: 2 additions & 0 deletions NOTICE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,5 @@ sharp under the Apache License Version 2.0
sinon under the BSD-3-Clause license
sinon-chai under the BSD-2-Clause license
uuid under the Massachusetts Institute of Technology (MIT) license
color under the Massachusetts Institute of Technology (MIT) license
color-name under the Massachusetts Institute of Technology (MIT) license
14 changes: 13 additions & 1 deletion deployment/serverless-image-handler.template
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
"Default" : 1,
"Type" : "Number",
"AllowedValues" : [ 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731, 1827, 3653 ]
},
"AutoWebP" : {
"Description" : "Would you like to enable automatic WebP based on accept headers? Select 'Yes' if so.",
"Default" : "No",
"Type" : "String",
"AllowedValues" : [ "Yes", "No" ]
}
},
"Metadata": {
Expand Down Expand Up @@ -74,6 +80,7 @@
"Resources": {
"Logs": {
"DeletionPolicy": "Retain",
"UpdateReplacePolicy": "Retain",
"Type": "AWS::S3::Bucket",
"Properties": {
"AccessControl": "LogDeliveryWrite",
Expand Down Expand Up @@ -130,7 +137,7 @@
"TargetOriginId": { "Fn::Sub": "${ImageHandlerApi}" },
"ForwardedValues": {
"QueryString": false,
"Headers": [ "Origin" ],
"Headers": [ "Origin", "Accept" ],
"Cookies": { "Forward": "none" }
},
"ViewerProtocolPolicy": "https-only"
Expand Down Expand Up @@ -310,6 +317,7 @@
},
"ImageHandlerApiDeployment": {
"Type": "AWS::ApiGateway::Deployment",
"DependsOn": "ApiAccountConfig",
"Properties": {
"RestApiId": { "Ref": "ImageHandlerApi" },
"StageName": "image",
Expand Down Expand Up @@ -371,6 +379,9 @@
"Timeout": 30,
"Environment" : {
"Variables" : {
"AUTO_WEBP" : {
"Ref" : "AutoWebP"
},
"CORS_ENABLED" : {
"Ref" : "CorsEnabled"
},
Expand Down Expand Up @@ -514,6 +525,7 @@
"Type": "AWS::S3::Bucket",
"Condition": "DeployDemoUICondition",
"DeletionPolicy": "Retain",
"UpdateReplacePolicy": "Retain",
"Properties": {
"BucketEncryption": {
"ServerSideEncryptionConfiguration": [
Expand Down
2 changes: 1 addition & 1 deletion source/custom-resource/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ let sendResponse = function(event, callback, logStreamName, responseStatus, resp
const responseBody = JSON.stringify({
Status: responseStatus,
Reason: reason,
PhysicalResourceId: logStreamName,
PhysicalResourceId: event.LogicalResourceId,
StackId: event.StackId,
RequestId: event.RequestId,
LogicalResourceId: event.LogicalResourceId,
Expand Down
2 changes: 0 additions & 2 deletions source/custom-resource/lib/s3-helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

'use strict';

let moment = require('moment');
let AWS = require('aws-sdk');
const fs = require('fs');

Expand Down Expand Up @@ -75,7 +74,6 @@ class s3Helper {
console.log(`Attempting to save content blob destination location: ${destS3Bucket}/${destS3key}`);
console.log(JSON.stringify(content));

let _self = this;
return new Promise((resolve, reject) => {
let _content = `'use strict';\n\nconst appVariables = {\n`;

Expand Down
123 changes: 101 additions & 22 deletions source/image-handler/image-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class ImageHandler {
if (edits !== undefined) {
const modifiedImage = await this.applyEdits(originalImage, edits);
if (request.outputFormat !== undefined) {
await modifiedImage.toFormat(request.outputFormat);
modifiedImage.toFormat(request.outputFormat);
}
const bufferImage = await modifiedImage.toBuffer();
return bufferImage.toString('base64');
Expand All @@ -42,26 +42,79 @@ class ImageHandler {
* @param {Object} edits - The edits to be made to the original image.
*/
async applyEdits(originalImage, edits) {
const image = sharp(originalImage);
if (edits.resize === undefined) {
edits.resize = {};
edits.resize.fit = 'inside';
}

const image = sharp(originalImage, { failOnError: false });
const metadata = await image.metadata();
const keys = Object.keys(edits);
const values = Object.values(edits);

// Apply the image edits
for (let i = 0; i < keys.length; i++) {
const key = keys[i];
const value = values[i];
if (key === 'overlayWith') {
const overlay = await this.getOverlayImage(value.bucket, value.key);
const params = [{ ...value.options, input: overlay }];
let imageMetadata = metadata;
if (edits.resize) {
let imageBuffer = await image.toBuffer();
imageMetadata = await sharp(imageBuffer).resize({ edits: { resize: edits.resize }}).metadata();
}

const { bucket, key, wRatio, hRatio, alpha } = value;
const overlay = await this.getOverlayImage(bucket, key, wRatio, hRatio, alpha, imageMetadata);
const overlayMetadata = await sharp(overlay).metadata();

let { options } = value;
if (options) {
if (options.left) {
let left = options.left;
if (left.endsWith('p')) {
left = parseInt(left.replace('p', ''));
if (left < 0) {
left = imageMetadata.width + (imageMetadata.width * left / 100) - overlayMetadata.width;
} else {
left = imageMetadata.width * left / 100;
}
} else {
left = parseInt(left);
if (left < 0) {
left = imageMetadata.width + left - overlayMetadata.width;
}
}
options.left = parseInt(left);
}
if (options.top) {
let top = options.top;
if (top.endsWith('p')) {
top = parseInt(top.replace('p', ''));
if (top < 0) {
top = imageMetadata.height + (imageMetadata.height * top / 100) - overlayMetadata.height;
} else {
top = imageMetadata.height * top / 100;
}
} else {
top = parseInt(top);
if (top < 0) {
top = imageMetadata.height + top - overlayMetadata.height;
}
}
options.top = parseInt(top);
}
}

const params = [{ ...options, input: overlay }];
image.composite(params);
} else if (key === 'smartCrop') {
const options = value;
const imageBuffer = await image.toBuffer();
const metadata = await image.metadata();
// ----
const boundingBox = await this.getBoundingBox(imageBuffer, options.faceIndex);
const cropArea = await this.getCropArea(boundingBox, options, metadata);
try { image.extract(cropArea) }
catch (err) {
const cropArea = this.getCropArea(boundingBox, options, metadata);
try {
image.extract(cropArea)
} catch (err) {
throw ({
status: 400,
code: 'SmartCrop::PaddingOutOfBounds',
Expand All @@ -82,18 +135,48 @@ class ImageHandler {
* @param {string} bucket - The name of the bucket containing the overlay.
* @param {string} key - The keyname corresponding to the overlay.
*/
async getOverlayImage(bucket, key) {
async getOverlayImage(bucket, key, wRatio, hRatio, alpha, sourceImageMetadata) {
const s3 = new AWS.S3();
const params = { Bucket: bucket, Key: key };
// Request
const request = s3.getObject(params).promise();
// Response handling
try {
const overlayImage = await request;
return Promise.resolve(overlayImage.Body);
const { width, height } = sourceImageMetadata;
const overlayImage = await s3.getObject(params).promise();
let resize = {
fit: 'inside'
}

// Set width and height of the watermark image based on the ratio
const zeroToHundred = /^(100|[1-9]?[0-9])$/;
if (zeroToHundred.test(wRatio)) {
resize['width'] = parseInt(width * wRatio / 100);
}
if (zeroToHundred.test(hRatio)) {
resize['height'] = parseInt(height * hRatio / 100);
}

// If alpha is not within 0-100, the default alpha is 0 (fully opaque).
if (zeroToHundred.test(alpha)) {
alpha = parseInt(alpha);
} else {
alpha = 0;
}

const convertedImage = await sharp(overlayImage.Body)
.resize(resize)
.composite([{
input: Buffer.from([255, 255, 255, 255 * (1 - alpha / 100)]),
raw: {
width: 1,
height: 1,
channels: 4
},
tile: true,
blend: 'dest-in'
}]).toBuffer();
return Promise.resolve(convertedImage);
} catch (err) {
return Promise.reject({
status: 500,
status: err.statusCode ? err.statusCode : 500,
code: err.code,
message: err.message
})
Expand Down Expand Up @@ -131,12 +214,9 @@ class ImageHandler {
const rekognition = new AWS.Rekognition();
const params = { Image: { Bytes: imageBuffer }};
const faceIdx = (faceIndex !== undefined) ? faceIndex : 0;
// Request
const request = rekognition.detectFaces(params).promise();
// Response handling
try {
const response = (await request).FaceDetails[faceIdx].BoundingBox;
return Promise.resolve(await response);
const response = await rekognition.detectFaces(params).promise();
return Promise.resolve(response.FaceDetails[faceIdx].BoundingBox);
} catch (err) {
console.log(err);
if (err.message === "Cannot read property 'BoundingBox' of undefined") {
Expand All @@ -158,4 +238,3 @@ class ImageHandler {

// Exports
module.exports = ImageHandler;

Loading

1 comment on commit 19cbc3c

@bretto36
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes thank you! So good. webp!

Would still love to be able to customise the headers see PR #182

Please sign in to comment.