Lambda architecture
This section covers how to use the solution when deployed with the Lambda architecture.
Use the demo UI
The solution provides an optional demo UI that you can deploy into your AWS account to display basic capability and functionality. With this UI, you can interact directly with the new image handler using images from the specified Amazon S3 buckets in your account.
Screenshot of demo UI showing image source, original image, editing options, preview, code, and encoded URL.
Follow this procedure to experiment with the supported image editing features, preview the results, and create example URLs that you can use in your applications:
-
Sign in to the AWS CloudFormation console
. -
Select the solution’s installation stack.
-
Choose the Outputs tab, and then select value for the DemoUrl key. The Dynamic Image Transformation for Amazon CloudFront Demo UI opens in your browser.
-
In the Image Source card, perform the following actions:
-
Specify a bucket name to use for the demo. The bucket you specify must be listed in the
SOURCE_BUCKETSenvironment variable of the AWS Lambda function. -
Specify an image key to use for the demo. You must include the file extension in the key.
-
-
Select Import. The original image appears in the Original Image card.
-
In the Editor card, adjust the image settings, and select Preview to generate the modified image. You can select Reset to revert the settings back to their original values.
Note
The Dynamic Image Transformation for Amazon CloudFront demo UI offers a limited set of image edits and doesn’t include the full scope of capabilities offered by the Image Handler API and the image URL signature. We recommended using your own frontend application for image modification.
Dynamically resize photos
This solution offers the following fit options to dynamically resize an image: cover, contain, fill, inside, and outside. Refer to the sharp documentation
const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.jpeg>", edits: { resize: { width: 200, height: 250, fit: "cover" } } })
If you use contain as the resize fit mode, you can specify the color of the fill by providing the hex code of the color you want to use. For example:
const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.jpeg>", edits: { resize: { width: 200, height: 250, fit: "contain", background: { r: 255, g: 0, b: 0, alpha: 1 } } } })
Edit images
You can use this solution to edit your images, such as rotating them or changing the coloring to negative. Refer to the sharp documentation
const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.jpeg>", edits: { negate: true } })
Restricted operations
Certain Sharp operations are restricted by the solution to help enhance security. This includes (but may not be limited to):
-
clone
-
metadata
-
stats
-
composite (Though this is permitted through the use of overlayWith)
-
certain output options
(Including toFile, toBuffer, tile and raw)
For an exact list of allow-listed Sharp operations, you can visit constants.ts
Use smart cropping
This solution uses Amazon Rekognition for face detection in images submitted for smart cropping. To activate smart cropping on an image, add the smartCrop property to the edits property in the image request.
-
smartCrop(optional, boolean || object) - Activates the smart cropping feature for an original image. If the value is
true, then the feature returns the first face detected from the original image with no additional options. For example:const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.jpeg>", edits: { smartCrop: true } })
The following smartCrop variables are shown in the following code sample:
smartCrop.faceIndex(optional, number) - Specifies which face to focus on if multiple are present within an original image. The solution indexes detected faces in a zero-based array from the largest detected face to the smallest. If this value isn’t specified, Amazon Rekognition returns the largest face detected from the original image. smartCrop.padding(optional, number) - Specifies an amount of padding in pixels to add around the cropped image. The solution applies the padding value to all sides of the cropped image.
const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.jpeg>", edits: { smartCrop: { faceIndex: 1, // zero-based index of detected faces padding: 40, // padding expressed in pixels, applied to all sides } } })
Note
smartCrop is not supported for animated (such as, GIF) images.
Use round cropping
This solution can crop images in a circular pattern. To activate round cropping on an image, add the roundCrop property to the edits property in the image request.
-
roundCrop(optional, boolean || object) - Activates the round cropping feature for an original image. If the value is true, then the feature returns a circular cropped image that’s centered from the original image and has a diameter of the smallest edge of the original image. For example:
const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.jpeg>", edits: { roundCrop: true } })
The following roundCrop variables are shown in the following code sample:
roundCrop.rx (optional, number) - Specifies the radius along the x-axis of the ellipse. If a value isn’t provided, the image handler defaults to a value that’s half the length of the smallest edge. roundCrop.ry (optional, number) - Specifies the radius along the y-axis of the ellipse. If a value isn’t provided, the image handler defaults to a value that’s half the length of the smallest edge. roundCrop.top(optional, number) - Specifies the offset from the top of the original image to place the center of the ellipse. If a value isn’t provided, the image handler defaults to a value that’s half of the height. roundCrop.left (optional, number) - Specifies the offset from the left-most edge of the original image to place the center of the ellipse. If a value isn’t provided, the image handler defaults to a value that’s half of the width.
const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.jpeg>", edits: { roundCrop: { rx: 30, // x-axis radius ry: 20, // y-axis radius top: 300, // offset from top edge of original image left: 500 // offset from left edge of original image } } })
Note
roundCrop is not supported for animated (such as, GIF) images.
Overlay an image
This solution can overlay images on top of others, for cases like watermarking copyrighted image. To overlay an image, add the overlayWith property to the edits property in the image request.
overlayWith(optional, object) - Overlays an image on top of the original. For example:
const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.jpeg>", edits: { overlayWith: { bucket: "<myImageBucket>", key: "<myOverlayImage.jpeg>", alpha: 0-100, // Opaque (0) to Transparent (100) wRatio: 0-100, // Ratio of the underlying image that the overlay width should be hRatio: 0-100, // Ratio of the underlying image that the overlay height should be options: { top: "-10p", left: 150 } } } })
The following overlayWith variables are shown in the previous code sample:
-
overlayWith.bucket (required, string) - Specifies the bucket that the overlay image should be retrieved from. This bucket must be present in the
SOURCE_BUCKETSparameter. -
overlayWith.key (required, string) - Specifies the object key that is used for the overlay image.
-
overlayWith.alpha (optional, number) - Specifies the opacity that should be used for the overlay image. This can be set from 0 (fully opaque) and 100 (fully transparent).
-
overlayWith.wRatio (required, number) - Specifies the percentage of the width of underlying image that the overlay image should be sized to. This can be set from 0 and 100, where 100 indicates that the overlay image has the same width as the underlying image.
-
overlayWith.hRatio (required, number) - Specifies the percentage of the height of underlying image that the overlay image should be sized to. This can be set from 0 and 100, where 100 indicates that the overlay image has the same height as the underlying image.
-
overlayWith.options.top (optional, number | string) - Specifies the distance in pixels from the top edge of the underlying photo that the overlay should be placed. A number formatted as a string with a
pat the end is treated as a percentage. -
overlayWith.options.left (optional, number | string) - Specifies the distance in pixels from the left edge of the underlying photo that the overlay should be placed. A number formatted as a string with a
pat the end is treated as a percentage.
Note
overlayWith is not fully supported for animated (such as, GIF) images. Instead, only the first frame will receive an overlay.
Overwrite animated status
This solution assumes that GIF files with multiple pages should be animated. If you’d like to indicate that a GIF should not be animated, or that another file type should be animated, include the animated property in the edits property in the image request.
-
animated (optional, boolean) - Overwrites the initial animated status of the image. If the value is
true, the solution will attempt to process the image as animated. For example:const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.webp>", edits: { animated: true } })
If it is
false, the solution will process the image as a still image. For example:const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.gif>", edits: { animated: false } })
Note
If an image does not have multiple pages, it will always be processed as still, regardless of the edits.animated property. The following filters are not supported for images that are animated: rotate, smartCrop, roundCrop, and contentModeration.
Activate and use content moderation
This solution can detect inappropriate content using Amazon Rekognition. To activate content moderation, add the contentModeration property to the edits property in the image request.
-
contentModeration (optional, boolean || object) - Activates the content moderation feature for an original image. If the value is true, then the feature detects inappropriate content using Amazon Rekognition with a minimum confidence that’s set higher than 75%. If Amazon Rekognition finds inappropriate content, the solution blurs the image. For example:
const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.jpeg>", edits: { contentModeration: true } })
The following contentModeration variables are shown in the following code sample:
-
contentModeration.minConfidence (optional, number) - Specifies the minimum confidence level for Amazon Rekognition to use. Amazon Rekognition only returns detected content that’s higher than the minimum confidence. If a value isn’t provided, the default value is set to 75%.
-
contentModeration.blur (optional, number) - Specifies the intensity level that an image is blurred if inappropriate content is found. The number represents the sigma of the Gaussian mask, where sigma = 1 + radius /2. For more information, refer to the sharp
documentation. If a value isn’t provided, the default value is set to 50. -
contentModeration.moderationLabels (optional, array) - Identifies the specific content to search for. The image is blurred only if Amazon Rekognition locates the content specified in the smartCrop.moderationLabels provided. You can use either a top-level category or a second-level category. Top-level categories include its associated second-level categories. For more information about moderation label options, refer to Content moderation in the Amazon Rekognition Developer Guide.
const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.jpeg>", edits: { contentModeration: { minConfidence: 90, // minimum confidence level for inappropriate content blur: 80, // amount to blur image moderationLabels: [ // labels to search for "Hate Symbols", "Smoking" ] } } })
Note
contentModeration is not supported for animated (such as, GIF) images.
Include custom response headers
This solution allows you to include headers you’d like returned alongside the response, as part of your request.
-
headers (optional, object) - Includes the provided headers in the response. Header should be written in Pascal-Case and cannot overwrite headers that would otherwise be present in the response (Except for Cache-Control).
const imageRequest = JSON.stringify({ bucket: "<myImageBucket>", key: "<myImage.jpeg>", headers: { "Cache-Control":"max-age=86400,public" "Custom-Header":"some-custom-value" } })
Note
A deny-list is maintained which restricts which headers can be included with this feature. Headers which may serve a purpose for the browser or are used to support authentication/authorization are included in this deny-list. For an exact list of the regular expressions which are restricted, visit constants.ts
The presence of the expires query parameter will cause the Cache-Control header to be overridden, regardless of any value provided in the headers field.
If your deployment is using the S3 Object Lambda Architecture , the headers at this link cannot be included as custom headers.
Include request expiration
This solution supports the expires query parameter, which is used to decide whether the Lambda should process a request. If an Expiry date is in the future, the request will be processed. If the Expiry date has already passed, the Lambda will return a 400 Bad Request error with the code: ImageRequestExpired.
Values in the expires query parameter are expected in the format: YYYYMMDDTHHmmssZ. For example: April 9th, 2024, at 10:30:15 UTC -4 would become 20240409T143015Z.
Request expiry is compatible with signatures, and should be included as part of the path when signing a request. For example:
const secret = '<YOUR_SECRET_VALUE_IN_SECRETS_MANAGER>'; const path = '/<YOUR_PATH>'; // Add the first '/' to path. const expires = 'expires=<YOUR_EXPIRY>'; const to_sign = `${path}?${expires}` const signature = crypto.createHmac('sha256', secret).update(to_sign).digest('hex');
Note
If the expires query parameter is being used in conjunction with any query parameter based edits. When generating a signature, please ensure that the query parameters are sorted. For more information, see Image URL signature.
Define the source bucket for the request
To define the bucket used when getting the image for a request, include s3:BucketName as a modification in your request. For example, if your source buckets were "test-bucket-1, the-other-test-bucket", to indicate that the-other-test-bucket should be used when processing an image, enter the following:
https://<ApiEndpoint>/s3:the-other-test-bucket/<image.jpeg>
Note
Using the s3:BucketName tag requires that the bucket chosen is part of the SourceBuckets provided upon deployment. For information on how to change the SourceBuckets after deployment, see Backward compatibility.
Resize an image
To resize an image, specify fit-in and the desired image size. For example, to resize a JPEG image to 300 pixels wide and 400 pixels tall, enter the following:
https://<ApiEndpoint>/fit-in/<300x400>/<image.jpeg>
Use filters
To use filters, specify a filter from the following table. For example, to blur a JPEG image, enter the following:
https://<ApiEndpoint>/filters:blur(7)/<image.jpeg>
Note
Some Thumbor filters aren’t supported in the current version of this solution. This might affect legacy users with advanced image request configurations. For notes about Thumbor compatibility and source image storage limitations, see Backward compatibility. For examples of filter usage, refer to the Thumbor documentation
| Filter name | Filter syntax |
|---|---|
|
Animated |
|
|
Autojpg |
|
|
Background color |
|
|
Blur |
|
|
Color fill |
|
|
Convolution |
|
|
Crop |
|
|
Equalize |
|
|
Grayscale |
|
|
Image format
|
|
|
No upscale |
|
|
Proportion |
|
|
Quality |
|
|
Resize |
|
|
RGB |
|
|
Rotate |
|
|
Sharpen |
|
|
Smart Crop |
|
|
Stretch |
|
|
Strip Exif |
|
|
Strip ICC |
|
|
Upscale |
|
|
Watermark |
|
Use multiple filters
To use multiple filters on an image, list them in the same section of the URL. Filters process the image in the order that you specify them. For example:
https://<api-endpoint>/fit-in/<300x400>/filters:<fill>(<00ff00>)/filters:<rotate>(<90>)/<image.jpeg>
Replace filters- with filters:
If you put /(filters-)/gm in REWRITE_MATCH_PATTERN and filters: in REWRITE_SUBSTITUTION, you can call
https://<your-CloudFront-distribution>/filters:rotate(90)/<your-image>
instead of
https://<your-CloudFront-distribution>/filters-rotate(90)/<your-image>
to rotate your image. In this example, the solution replaces filters- (filters hyphen syntax) with filters: (filters colon syntax).
Reverse path order
You can place filters at the end of the path rather than before the image key.
-
Use the
REWRITE_MATCH_PATTERNwith a regular expression that parses the path into two groups. The solution then usesREWRITE_SUBSTITUTIONto switch the order of the groups. -
Use a regular expression specified by
REWRITE_MATCH_PATTERNto parse the path into groups for a request likehttps://abcd.cloudfront.net/imagekey.png/fit-in/200x200, where the image key appears before the filters. For example:REWRITE_MATCH_PATTERN = /^\/(.*?\..*?)\/(.+)$/gm
-
Reverse the order of the fields with
REWRITE_SUBSTITUTIONto convert the request into a Thumbor style request likehttps://abcd.cloudfront.net/fit-in/200x200/imagekey.png, where the image key is moved to the end of the request. For example:REWRITE_SUBSTITUTION = /$2/$1
Parse request type
Refer to image-request.spec.js.file