Skip to content

AWS Lambda for resizing images in S3 on fly.

License

Notifications You must be signed in to change notification settings

subpublic/s3-resizer

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

What is it?

It's AWS Lambda, which is a compute service that lets you run code without provisioning or managing servers.

Read more about AWS Lambda.

Demo

https://sagidm.github.io/smartuploader/examples/4.s3-resizer.html

What this lambda provides

Let's say we have some shared image in S3, for example:
https://example.com/images/pretty_photo.jpg

to resize this image to 150x150, for instance, on fly we can make a request like this:
https://example.com/images/150x150/pretty_photo.jpg

So, if there's not image in this path, it's redirected to lambda and, after a moment, lambda creates the suitable image and then redirects back. We'll obviously have a new image next time.


Instead of WxH there're some extra available magic paths:
.../AUTOx150/...
.../150xAUTO/...
or
.../150x150_max/...
.../150x150_min/...

Note that s3-resizer don't enlarge an image if the original image width or height are already less than required dimensions. You can read about #withoutEnlargement method.

Setting up

To resize images we need a storage, which is S3, and Lambda function. Then we should set up redirection rules.

  • Create a Bucket

    • Click on the blue button Create bucket
    • Enter the name and click on Create
  • Create a Lambda

    • Click on the orange button Create a function
    • In the next page, click on the similar button Author from scratch
    • Add a trigger, which would listen to http requests (you also would be able to do it later)
      • On the dotted square choose API Gateway
      • You can use default API name or create new one
      • In Security select Open, then click Next
    • In Configure function page
      • Name a new lambda
      • In Runtime select Node.js 8.10
      • Upload a .zip file (download it from releases)
      • You'll also need to set up two Environment variables, with BUCKET and URL as keys. But in this time, you don't know about that URL. It is endpoint which you'll see below.

      • Choose role which has permission to put any object or create a new one. To do that
        • choose Create a custom role in role's list. It should open a new page in your browser. On that page
        • choose Create a new IAM Role
        • name you role, for example: "access_to_putObject"
        • Expand View Policy Document, click Edit, and write this content:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "logs:CreateLogGroup",
        "logs:CreateLogStream",
        "logs:PutLogEvents"
      ],
      "Resource": "arn:aws:logs:*:*:*"
    },
    {
      "Effect": "Allow",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::__BUCKET_NAME__/*"
    }
  ]
}

Pay attention to __BUCKET_NAME__

      • That page should closes after that action. So go on creating a lambda. And take a look at Advanced settings
        • Allocate 768mb memory
        • Timeout could be 5 seconds

It's mooore than enough. But you shouldn't care of limits because images caches, which means lambda is called only for the first time. For example, large png 29mb image converts to 150x150 in 1.9s with 1024mb memory allocated, 3.7 with 512mb, and 7.2s with 256. (I guess these such different results is because of GC). For normal images, results are nearly the same (400-700 mls).

      • Click Next, Create function. And wait for 20-30 seconds. Lambda is created.

  • Public access to files in your bucket and relationships between lambda and bucket.
    • Firstly, you need Lambda's url
      • Click the link of API name (in case you didn't change it in creating lambda, it should name like LambdaMicroservice)
      • On the new page, look for Actions button, select Deploy API and choose prod in Deployment stage. Then click Deploy
      • Expand prod in Stages, click on GET and copy URL that you see
    • Open your created bucket -> Permissions -> Bucket Policy
    • Paste this pease of code there and click Save
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::__BUCKET_NAME__/*"
        }
    ]
}

Pay attention to __BUCKET_NAME__. By the way, you're able to open access not to whole bucket but to specific directory specifying it instead of *.

    • Go to Properties (near to Permissions) -> Static website hosting -> Select "Use this bucket to host a website"
    • In Index document paste any file, it'd be logical to name it "index.html"
    • Paste this Redirection rules
<RoutingRules>
  <RoutingRule>
    <Condition>
      <KeyPrefixEquals/>
      <HttpErrorCodeReturnedEquals>404</HttpErrorCodeReturnedEquals>
    </Condition>
    <Redirect>
      <Protocol>https</Protocol>
      <HostName>__DOMAIN__</HostName>
      <ReplaceKeyPrefixWith>__PATH_TO_LAMBDA__?path=</ReplaceKeyPrefixWith>
      <HttpRedirectCode>307</HttpRedirectCode>
    </Redirect>
  </RoutingRule>
</RoutingRules>

Pay attention to __DOMAIN__ and __PATH_TO_LAMBDA__ (protocol is always https)
For example, lambda's url is https://some-id.execute-api.us-east-1.amazonaws.com/prod/your-lambdas-name, the correct xml nodes must looks like

<HostName>some-id.execute-api.us-east-1.amazonaws.com</HostName>
<ReplaceKeyPrefixWith>prod/your-lambdas-name?path=</ReplaceKeyPrefixWith>
    • At this state, copy your Endpoint and click save
    • Go to your lambda -> Code and set up these two Environment variables (format: key=value)
      BUCKET=your bucket's name
      URL=Endpoint you copied before
    • Save it. You've done!

  • Test your lambda (optional)
    • Upload an image to your bucket and copy link to it. Check if the image shows in your browser

Attention. That link must be of your Endpoint (website hosting). It make by concatinating "$endpoint_url/$path_to_image/$image_name"

    • Go to lambda, click on Test, and paste this json:
{
  "queryStringParameters": {"path": __YOUR_IMAGE_PATH_WITH_SIZE_PREFIX__}
}

__YOUR_IMAGE_PATH_WITH_SIZE_PREFIX__ - for example: 150x150/pretty_image.jpg

    • Go back to the bucket, a new directory 150x150 must be created

Some patterns

This is some pattern which you can use in the models:

IMAGE_PATH = "#{YOUR_ENDPOINT_URL}/uploads/images/models/"

def images()
  img = self.image_name
  
  return {
    original: "#{IMAGE_PATH}#{img}",
    big: "#{IMAGE_PATH}1000x1000_max/#{img}",
    small: "#{IMAGE_PATH}450x450_max/#{img}",
    thumb: "#{IMAGE_PATH}128x128/#{img}"
  }
end

About

AWS Lambda for resizing images in S3 on fly.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • JavaScript 100.0%