# Image resizing on the fly with AWS Lambda, API Gateway, and S3 Storage

- URL: https://www.obytes.com/blog/image-resizing-on-the-fly-with-aws-lambda,-api-gateway,-and-s3-storage
- Published: 2019-08-20
- Author: Mohammed SAFI
- Topics: DevOps, Back-end
- Summary: Build a serverless image resizing service on the fly with AWS Lambda, API Gateway and S3 that creates and serves the requested sizes without EC2 servers.

In this article, we are going to talk about creating different image sizes after requesting the original one with size parameters (width/height) from AWS S3 storage.

Images are essential to creating an engaging user experience, more effort might be required to integrate large and high-resolution images into the user interface, but the problem here is that they can slow down the download speed of the entire web page. Imagine that you have a blogs site, and most readers are viewing your site on their phones, in this case, they don’t need the high-resolution pictures, this may slow downloading the whole content which is not good for your readers, and some of them are reading from their laptops where they have a better connection and a better screen, and so will appreciate the higher-quality images.

The main goal of this post is to show you how you can use only the AWS lambda function, which means no servers (EC2 instances) is needed, to create an image resizing task. When an image gets loaded from the s3 bucket through an API Gateway endpoint, the lambda function will be triggered which will resize the image (if the image with the specified size does not exist in the bucket) based on the specified size, and it will return the resized image URL.

## Table of contents

1. Architecture
2. Lambda Function Configuration
3. Upload Source Code
4. API Gateway Configuration
5. Testing

### Architecture

![Architecture Diagram](https://www.obytes.com/assets/posts/2019-08-15-image-resizing-on-the-fly-with-aws-lambda,-api-gateway,-and-s3-storage/images/architecture-diagram.png)

1. Users will send a request to the API Gateway with the image size parameters(width/height) in order to receive the s3 URL of the resized image,
2. The API Gateway request will trigger a lambda function,
3. that will check if the image with the given size exists or not,
4. if it does this function will return the image s3 URL,
5. else it will take the original image from s3, resize it with the given size and return back the image s3 URL.

### Lambda Function Configuration

First of all, we assume that you already have an s3 bucket and API gateway instance created, so our images resizing lambda function will need at the minimum these configurations:

- Execution role: AWS S3 Full Access, and AWS Basic Execution Role

![Lambda Function Configuration Execution Role](https://www.obytes.com/assets/posts/2019-08-15-image-resizing-on-the-fly-with-aws-lambda,-api-gateway,-and-s3-storage/images/lambda-function-configuration-execution-role.png)

- Basic settings: Timeout 20 sec
- Add an API Gateway trigger to the lambda function with the existing API

![Lambda Function Configuration Lambda Trigger](https://www.obytes.com/assets/posts/2019-08-15-image-resizing-on-the-fly-with-aws-lambda,-api-gateway,-and-s3-storage/images/lambda-function-configuration-lambda-trigger.png)

### Upload Source Code

We are going to use Python programming language to build our image resizing lambda function,

> SOURCE CODE: <https://github.com/obytes/resize_s3_images>

Let us start by explaining the source code:

```python
def lambda_handler(event, context):
    key = event['queryStringParameters'].get('key', None)
    size = event['queryStringParameters'].get('size', None)

    image_s3_url = resize_image(os.environ['BUCKET'], key, size)

    return {
        'statusCode': 301,
        'body': image_s3_url
    }
```

The `lambda_handler` will get called when our lambda function got triggered by a new incoming request for an image to be resized. It gets the `key` and `size` arguments from the request path, then it calls the `resize_image` function with the previous arguments and the s3 bucket name in order to get where our new resized image is.

Now let’s get deeply under our `resize_image` function, first it gets the original from the given s3 bucket if exists:

```python
def resize_image(bucket_name, key, size):
    size_split = size.split('x')
    s3 = boto3.resource('s3')
    obj = s3.Object(
        bucket_name=bucket_name,
        key=key,
    )
    obj_body = obj.get()['Body'].read()
```

Second, it resizes the image using `PILLOW` package:

```python
img = Image.open(BytesIO(obj_body))
img = img.resize((int(size_split[0]), int(size_split[1])), PIL.Image.ANTIALIAS)
buffer = BytesIO()
img.save(buffer, 'JPEG')
buffer.seek(0)
```

Finally, it uploads the resized image back to s3, and returns its URL:

```python
resized_key="{size}_{key}".format(size=size, key=key)
obj = s3.Object(
    bucket_name=bucket_name,
    key=resized_key
)
obj.put(Body=buffer, ContentType='image/jpeg')

return "https://{bucket}.s3.amazonaws.com/{resized_key}".format(bucket=bucket_name, resized_key=resized_key)
```

If we try to create a deployment package and upload it to lambda like it is documented in aws [here](https://docs.aws.amazon.com/lambda/latest/dg/lambda-python-how-to-create-deployment-package.html), it is not going to work As we can see, it depends on [PIL](https://pillow.readthedocs.io/en/stable/), a library for manipulating images that it is not on the Lambda environment nor on the standard Python Library. How can we fix this?
Using Python Wheels! [Wheels](https://www.obytes.com/blog/pythonwheels.com/) are the new standard of python distribution. It creates Wheels packages ready to run everywhere. Since Lambda runs on the Amazon Linux Distribution, we can try [this](https://pypi.python.org/packages/be/6e/5084627fee802fa6a13741ff988e34f2d2ee25e8a6a276b4832f278c5654/Pillow-4.2.0-cp36-cp36m-manylinux1_x86_64.whl#md5=http://a365fff413fe338398b6-1c8a9b3114517dc5fe17b7c3f8c63a43.r19.cf2.rackcdn.com/Pillow-6.1.0-cp36-cp36m-manylinux1_x86_64.whl) version. After downloading it, extract on the same directory as the `resize_s3_images.py` file and zip it all together!

```bash
$ ls
Pillow-6.1.0-cp36-cp36m-manylinux1_x86_64.whl resize_s3_images.py
$ unzip Pillow-6.1.0-cp36-cp36m-manylinux1_x86_64.whl && rm Pillow-6.1.0-cp36-cp36m-manylinux1_x86_64.whl
$ zip -r resize_s3_images.zip .
```

On the AWS Lambda dashboard upload the zipped file `resize_s3_images.zip`

![Upload Source Code](https://www.obytes.com/assets/posts/2019-08-15-image-resizing-on-the-fly-with-aws-lambda,-api-gateway,-and-s3-storage/images/upload-source-code.png)

### API Gateway Configuration

Next, it is time to setup an api gateway endpoint:

- Root path `/`
- Resource in root path called `/resize`
- GET method in the previous resource that should be integrated with our lambda function we created before
- Enable CORS for the previous http GET method

![API Gateway Configuration](https://www.obytes.com/assets/posts/2019-08-15-image-resizing-on-the-fly-with-aws-lambda,-api-gateway,-and-s3-storage/images/api-gateway-configuration.png)

You should deploy the API in order to make the whole changes live

### Testing

Now we have built everything we need for our image resizing lambda function and let’s test it using [cURL](https://curl.haxx.se/docs/manpage.html).

![Testing gif](https://www.obytes.com/assets/posts/2019-08-15-image-resizing-on-the-fly-with-aws-lambda,-api-gateway,-and-s3-storage/images/testing-gif.gif)
