Cloudfront and S3 template for thumbnail generation and caching for Yapawa
S3 Bucket as website holding cached files. When a file is not found, returns 307 towards an API Gateway.
API Gateway will generate the thumbnail and store in the cache bucket for subsequent request and return a 302 with the Cloudfront URL.
Edit config/dev.yml and config/production.yml to suit your needs.
Run nvm use
to load the right node version and npm install
to install all the dependencies.
- cacheBucket: Bucket name to hold cached images
- srcBucket: Bucket holding originals as created by Amplify
- srcPrefix: Prefix for stored images without trailing slash (public by default)
Inspired from Cloudinary and ImageKit
https://{domain}/{albumId}/{photoId}/{filename}/{version}/{transformations}/{name}.{format}
- domain: Cloudfront domain
- albumId: The AlbumId of the photo
- photoId: The Id ID of the photo
- filename: Photo source filename
- version: allows cache busting
- transformations: see Cloudinary
- w: width in pixels
- h: height in pixels
- c: crop mode
- scale: Resizes the image to exactly match the width and height, changing the aspect ratio.
- crop: Resizes and crops the image to width and height. Cropped area depends on gravity. (default when both dimensions is passed)
- fit: Resizes the image to fit inside width and height (default when only one dimension is passed).
- fill: Resizes the image to cover both width and height.
- pad: Resizes the image to fit inside width and height and add padding to match width and height.
- cover: Crops the image to width and height. Cropped area depends on gravity.
- ar: Aspect Ratio. When only one dimension is set, sets the other dimension.
- g: gravity (default center), valid values are:
top
,right top
,right
,right bottom
,bottom
,left bottom
,left
,left top
,north
,northeast
,east
,southeast
,south
,southwest
,west
,northwest
,center
orcentre
. - dpr: Changes image size to match dpr
- bg: Background color (RGB code) when crop mode is pad, default black
- name: Photo slug
- format: output format (jpg, jpeg, png or webp)
sls deploy
(development) or sls -s production deploy
-
No auto detection on DPR, client needs to know it's DPR.
This can be done using Javascript:
let dpr = 1 if (devicePixelRatio) { dpr = devicePixelRatio } else if (window.devicePixelRatio) { dpr = window.devicePixelRatio } dpr = parseFloat(parseFloat(dpr).toFixed(1))
-
No auto detection on supported formats: client needs to know if he can display webp.
This can be done using Javascript:
let supportsWebp = false if (!self.createImageBitmap) { supportsWebp = false } const webpData = 'data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAAAAAAfQ//73v/+BiOh/AAA=' return fetch(webpData).then(r => { return r.blob().then(blob => { return createImageBitmap(blob).then(() => { supportsWebp = true }, () => { supportsWebp = false }) }) })
-
No normalization for transformations
/w_100,h_100/
and/w_101,h_100/
will generate different files in S3 and execute lambda twice/w_100,h_100/
and/h_100,w_100/
will generate different files in S3 and execute lambda twice
This can be mitigated by using size names instead of pixels:
- `/s_medium/` would tell Lambda to generate an image of 128x128
-
Works only with clients following redirection Not really a problem, your client is a browser, and all of them follow redirection
Based on serverless-template-aws-webpack-nodejs
- events/ Store all events related to testing
- lib/config.js Javascript module to build serverless.yml
- resources/
Contains yml files describing each resource. Definitions can be nested 2 levels deep, in a subfolder describing the AWS resource, like
IamRole/specificServiceRole.yml
. The folder name is expected to follow Serverless convention for naming. - services/ Contains each individual Lambda function (.js) and it's definitions (.yml). In addition to the usual handler and event definitions, the yml can also hold a specific resource definition related to the function, without the need for an entry in the resources/ folder.
- stages/ Stage specific configurations.
lambda-log provides a more structured way of logging:
const log = require('lambda-log')
log.info('Log Tag', {key1: value1, key2: value2})
Which will result in:
{"_logLevel":"info","msg":"Log Tag","key1":"value1","key2":"value2","_tags":["log","info"]}
You can also add meta data by default:
log.options.meta.fct = 'fctName'
log.options.meta.requestId = event.requestContext.requestId
log.options.meta.path = event.path
log.options.meta.sourceIp = event.requestContext.identity.sourceIp