This deployment/process ensures that cloudfront can deliver images on the fly which are resized based on the given dimensions, and in the format needed.
For now, this supports only webp
or png
format.
- Say there is an image in your S3 bucket, at address
s3://folder/image.jpg
, then it'll be available on cloudfront atCLOUDFRONT_URL/folder/image.jpg
- To get a resized image, go to
CLOUDFRONT_URL/{width}x{height}/folder/image.jpg
- To get a resized imagedd, in webp format, go to
CLOUDFRONT_URL/webp/{width}x{height}/folder/image.jpg
- To get a resized image with only one dimension, say height, pass the other as 0. This would always maintain the aspect ratio
- To convert an image to webp, without resizing it, pass both height and width as 0.
The broad flow is as follows:
- User requests an image from cloudfront, passing paramters for width, height and format in the URL
- Cloudfront, delivers image if it has it in cache, else goes to step 3
- Cloufront, looks up a S3 bucket to check if resource is available
- If resource available in S3, with the same size/format, cloudfront would return that. Else, step 4.
- If S3 does not have that resource, then using S3 Website Hosting, and Rerouting Rules, the 404 URL is redirected an an
API Gateway
- The API Gateway, invokes a Lambda, which would read the height, width and format, and create the resource on S3, and redirect back to that URL
- Once
aws-cli
is installed, ensure it's configured with the right access keys usingaws configure
. Ensure that the user here, has permission to: a. Create Lambda Functions, API Gateway b. Read/Write from S3 Buckets (at lease the one you are operating with) c. Can create Cloudwatch Log Groups (Optional: So that you can see logs) - Create S3 bucket, if not already. Let's call it
SOURCE_BUCKET
. Ensure you don'tBlock Public Access
as well as use the Policy from the appendix to make items public. - Set up
Static Website Hosting
for it, and note the Hosted URL. Let's call thatSOURCE_BUCKET_HOSTED_URL
- Create Cloudfront Distribution with
SOURCE_BUCKET_HOSTED_URL
as theORIGIN
. Once done, note the URL of the distribution. Let's call thatCLOUDFRONT_URL
- (Optional) You can map a custom domain to this
CLOUDFRONT_URL
- Open the Repo, and via shell, head to
resizer
folder - Configure Lambda method by editing the
serverless.yml
. Things to configure: a.BUCKET
: Name ofSOURCE_BUCKET
b.URL
:CLOUDFRONT_URL
- Run
npm install
(This would install all needed packages) - This would install the wrong version of
sharp
the library used to resize images. Since you most likely aren't using the same OS as Lambda. To fix this, refer to appendix. - Run
sls deploy
- Once the deploy is done, this would also create an API Endpoint. Note that URL. Let's call that
API_GATEWAY_URL
. This would of the formhttps://{some_id}.execute-api.{aws-region}.amazonaws.com/{stage}/resizer
. Let's split this into two parts. Let's call them: a.API_GATEWAY_HOST
:{some_id}.execute-api.{aws-region}.amazonaws.com
(Note, nohttps
)
a.API_GATEWAY_PATH
:{stage}/resizer
- From the appendix, copy the
S3 Routing Rules Template
and replace these two variables. This can be pasted in theS3 Static Website Hosting
section on S3, under Rewriting Rules - To avoid Cloudfront from caching the redirect, we have to set custom TTL on cloudfront. Set Minimum and Default to 0. (Since the assets added via this lambda automatically have a TTL/Max-age, those would get overwritten, just not for the redirects)
- Please note, that if you are pushing images to this S3 bucket, make sure you explicitly set a max-age
Run the following two commands:
rm -rf node_modules/sharp
npm install --arch=x64 --platform=linux sharp
Don't forget to replace BUCKET_NAME
with your BUCKET_NAME
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": "arn:aws:s3:::{{BUCKET_NAME}}/*"
}
]
}
<RoutingRules>
<RoutingRule>
<Condition>
<KeyPrefixEquals/>
<HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
</Condition>
<Redirect>
<Protocol>https</Protocol>
<HostName>{{API_GATEWAY_HOST}}</HostName>
<ReplaceKeyPrefixWith>{{API_GATEWAY_PATH}}?key=</ReplaceKeyPrefixWith>
<HttpRedirectCode>307</HttpRedirectCode>
</Redirect>
</RoutingRule>
</RoutingRules>
- To @pavangowdasu with a lot of JS code! And in general, helping me with code always!
- This is based on this hackernoon post by @manjillama
Feel free to fork the repo and submit a pull request. I'm fairly novice at Javascript so I'm pretty sure someone else can seriously improve this.