5

I'm trying to get my image from S3 bucket and return it. Here's the code:

import base64
import boto3
import json
import random

s3 = boto3.client('s3')

def lambda_handler(event, context):
    number = random.randint(0,1)
    if number == 1:
        response = s3.get_object(
            Bucket='bucket-name',
            Key='image.png',
        )
        image = response['Body'].read()
        return {
            'headers': { "Content-Type": "image/png" },
            'statusCode': 200,
            'body': base64.b64encode(image).decode('utf-8'),
            'isBase64Encoded': True
        }
    else:
        return {
            'headers': { "Content-type": "text/html" },
            'statusCode': 200,
            'body': "<h1>This is text</h1>",
        }

When I hit my endpoint, an image of a tiny white box is returned. I know image.png exists in my bucket, and when I use the web GUI to open it in my browser, the image is loaded properly. What am I exactly doing wrong here? And in case it matters, here's how I'm uploading the image to S3 (from another Lambda):

...

# Prepare image for S3
buffer = io.BytesIO()
my_image.save(buffer, 'PNG')
buffer.seek(0) # Rewind pointer back to start
    
response = s3.put_object(
    Bucket=S3_BUCKET_NAME,
    Key=f'{S3_KEY}{filename}.png',
    Body=buffer,
    ContentType='image/png',
)

...

In the above code, my_image is just an image I created using the PIL library.

Thanks for any help!

darkhorse
  • 8,192
  • 21
  • 72
  • 148
  • How large is the image? Note that both API Gateway and AWS Lambda have payload size limits. Your code seems to be the [canonical example from AWS](https://docs.aws.amazon.com/apigateway/latest/developerguide/lambda-proxy-binary-media.html) which I would assume works fine, so I suspect that you have configured something incorrectly, e.g. Lambda proxy integration. – jarmod Nov 04 '21 at 01:14
  • @jarmod Around 100kb. Can I increase the payload size limits? – darkhorse Nov 04 '21 at 01:16
  • 1
    No, they are hard limits (10MB for API Gateway, 6MB for Lambda iirc) but 100KB, even base64-encoded, will be fine. You should use pre-signed URLs if you need larger (uploads or downloads). – jarmod Nov 04 '21 at 01:18
  • Why do you base64.decode it? Your upload procedure does not involve any base64 encoding. – Marcin Nov 04 '21 at 02:06
  • @Marcin How else would you return it? I was following a tutorial from AWS docs. – darkhorse Nov 04 '21 at 02:27
  • This lambda code works as-is for me. If you manually upload the image using the CLI, does it work? – Anon Coward Nov 04 '21 at 02:32
  • How did you setup your API gateway and how do you use the API gateway url to shown the image in the browser? Please update the question with all the details. – Marcin Nov 04 '21 at 02:43
  • @AnonCoward It doesn't when I use the GUI to upload an image either. Does that work for you as is? Then I'm fairly certain it's an issue with the API configurations. – darkhorse Nov 04 '21 at 03:13
  • 1
    Yes, it works regardless of how I upload the image – Anon Coward Nov 04 '21 at 03:39
  • How did it go? The issue still persists? – Marcin Nov 05 '21 at 00:21

1 Answers1

3

Here it is how I do this:

Your lambda with corrected body:

import base64
import boto3
import json
import random

s3 = boto3.client('s3')

def lambda_handler(event, context):

    response = s3.get_object(
        Bucket='bucket-name',
        Key='image.png',
    )
    image = response['Body'].read()

    return {
        'headers': { "Content-Type": "image/png" },
        'statusCode': 200,
        'body': base64.b64encode(image),
        'isBase64Encoded': True
    }

API gateway settings

enter image description here

Integration Request

enter image description here

Marcin
  • 215,873
  • 14
  • 235
  • 294