Build image resizer using AWS lambda-serverless framework
We will build an instant image resizer using AWS lambda and S3 events. When a new image is uploaded to a specific bucket, we will catch it, resize it and re-upload it to S3.
Create the function
Create the function by the command:
sls create -t aws-python3 -p thumbnail-generator
Now, create virtualenv
locally.
virtualenv venv -p python
It will create a folder named venv
in your root folder. Activate the virtualenv
by source venv/bin/activate
. Now, install two packages called boto3 and pillow:pip install boto3 pillow
Serverless framework plugin
To use a third-party package in your functions, we need to install a plugin called serverless-python-requirements
You will see a plugins block in serverless.yml
. Create a file requirements.txt
and write our package's name.
Handler function
Make our function name s3_thumbnail_generator
in the handler.py
file.
import boto3
from io import BytesIO
from PIL import Image, ImageOps
import os
s3 = boto3.client('s3')
size = 128
def s3_thumbnail_generator(event, context):
records = event.get('Records', [])
if len(records):
bucket = records[0].get('s3', {}).get('bucket', {}).get('name')
key = records[0].get('s3', {}).get('object', {}).get('key')
# only create a thumbnail on non thumbnail pictures
if bucket and key:
# get the image
image = get_s3_image(bucket, key)
# resize the image
thumbnail = image_to_thumbnail(image)
# get the new filename
thumbnail_key = new_filename(key)
# upload the file
url = upload_to_s3(bucket, thumbnail_key, thumbnail)
return url
From the event param, we will get Records that comes from the s3 notification event. Then we extract the bucket and file name.
Here are the utils to handle image processing:
def get_s3_image(bucket, key):
print("Getting image from S3 with key: {}".format(key))
response = s3.get_object(Bucket=bucket, Key=key)
image_content = response['Body'].read()
img = Image.open(BytesIO(image_content))
return img
def image_to_thumbnail(image):
return ImageOps.fit(image, (size, size), Image.ANTIALIAS)
def new_filename(key):
key_split = key.rsplit('.', 1)
return key_split[0] + "_thumbnail.png"
We fetch the image from the bucket, convert the image to a thumbnail, then generate the filename.
To re-upload the converted image:
def upload_to_s3(bucket, key, image):
out_thumbnail = BytesIO()
image.save(out_thumbnail, 'PNG')
out_thumbnail.seek(0)
response = s3.put_object(
# ACL='public-read',
Body=out_thumbnail,
Bucket=bucket,
ContentType='image/png',
Key=key
)
url = '{}/{}/{}'.format(s3.meta.endpoint_url, bucket, key)
return url
Here is the serverless.yml:
# Welcome to Serverless!
service: thumbnail-generator
frameworkVersion: '3'
provider:
name: aws
runtime: python3.8
region: us-east-1
profile: rumi # IAM user
timeout: 10
memorySize: 128
iamRoleStatements:
- Effect: "Allow"
Action:
- "s3:*"
Resource: "*"
custom:
bucket: s3-thumbnail-generator
pythonRequirements:
dockerizePip: true
functions:
thumbnail_generator:
handler: handler.s3_thumbnail_generator
events:
- s3:
bucket: ${self:custom.bucket}
event: s3:ObjectCreated:*
plugins:
- serverless-python-requirements
Deployment
To deploy the function run the below command:sls deploy
It will bundle the package and then deploy it to s3. You can check your buckets. Now, go to the s3 bucket and upload an image. It will generate a thumbnail instantly.