Skip to content

Instantly share code, notes, and snippets.

@jbsulli
Created September 15, 2017 00:15
Show Gist options
  • Save jbsulli/8bb9be66d4d09a58b5a48497b7f8abb1 to your computer and use it in GitHub Desktop.
Save jbsulli/8bb9be66d4d09a58b5a48497b7f8abb1 to your computer and use it in GitHub Desktop.

What I learned - Image Resize on Cache Miss

The task:

I really wanted a way to create resized images on the fly just by specifying the size of the image in the request URL. So, if I had the image kitty.jpg but wanted to resize it to be 100px by 100px, would simply request kitty-100x100.jpg, the server would realize it doesn't exists and create it, and the resized image would be returned to me.

The solution:

I was following this guide written by John Pignata. It is an interesting solution and fit my needs but I did run into some caveats. Below are some of the gotchas I encountered.

1) There are two S3 domains you can point your CloudFront distribution at:

  1. my-bucket.s3.amazonaws.com
  2. my-bucket.s3-website-us-west-2.amazonaws.com

The first is what CloudFront shows as a dropdown value when you're creating an origin. The second is what you ACTUALLY want if you are trying to do redirects. The second you will find at the top of the dialog box when you setup S3 static website hosting. What seems to be happening is the first domain is a "vanilla" S3 and the second is an S3 website with additional functionality (like redirects... which we need).

2) 307s are cached

When I finished my implementation, I found my initial redirect was being cached. This caused my browser to keep hitting the resize lambda... not good. To remedy this, I had to set the Default TTL for my CloudFront Origin to 0 and specify a CacheControl value when uploading my resized image to S3. Although it's not ideal, it works for me because I have a CloudFront Origin that is only for handling images and the only images available are the resized ones; otherwise, setting a default cache of 0 would not be a good idea.

3) Round your width & height

In your lambda, make sure you do a Math.floor() (or round or ceil). I took this a step further and implemented the scaling factor technique shown in this article. This allowed me to force images to only scale down and have a max width/height:

const MAX_HEIGHT = 4472;
const MAX_WIDTH = 4472;

//...

var scalingFactor = Math.min(
    (desired_width || MAX_WIDTH) / metadata.width,
    (desired_height || MAX_HEIGHT) / metadata.height,
    1 // don't scale up
);

let width  = Math.floor(scalingFactor * metadata.width);
let height = Math.floor(scalingFactor * metadata.height);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment