1

Season's greetings,

We have a lambda that:
Step 1: Downloads image from S3
Step 2: Resizes and formats the image using sharp
Step 3: Return a 200 response with the image in body

like so:

const sharpPipe = sharp()
    .resize(300, 300)
    .toFormat('webp');            

const getParams = {
    Bucket: myBucket,
    Key: imageKey
}

const s3Response = await s3Client
    .send(new GetObjectCommand(getParams));

const bodyStream = s3Response.Body;

const imageBuffer = await bodyStream.pipe(sharpPipe).toBuffer();

response = {
    statusCode: 200,
    headers: {
        "Content-Type": 'image/webp',
    },
    body: imageBuffer,
    isBase64Encoded: false,
};

However, this does not render when you hit the Lambda function URL in Chrome or Postman.

I know I can convert the image to base64 and that works fine but it slows down the API significantly and that is an issue in this case.

What other format, that is fast to convert to, can I return the image in that will be rendered by a browser or Postman?

P.S. I did try application/octet-stream like so:

response = {
    statusCode: 200,
    headers: {
        "Content-Type": `application/octet-stream`
    },
    body: imageBuffer.toString('binary'),
    isBase64Encoded: false,
};

But that returned gibberish

Izak Joubert
  • 906
  • 11
  • 29

1 Answers1

0

AWS has an example (Return binary media from a Lambda proxy integration) that does in fact recommend converting to base64. I believe this step may be necessary because of the way the request gets passed through other services (such as API Gateway) before getting sent to the browser.

Following that example, you would set the body to the base64-encoding of the image binary, and set isBase64Encoded to true.

To improve performance, consider changing your process to save the resized image back to S3 and not serving it through Lambda on future requests. AWS has a blog post describing how to set this up:

Resize Images on the Fly with Amazon S3, AWS Lambda, and Amazon API Gateway

This way, any slight performance hit in the resize step only affects the image the first time it is needed. Subsequent requests for it are served as fast as any other images served directly from S3.

To further improve performance, you could consider proactively generating the resized image as soon as the original is uploaded to S3. This way, even the first request for the resized image will be fast. You could do this programmatically in the code that uploads the original image. Or you could set up a trigger to execute a Lambda whenever a new image is uploaded.

Tyler Ham
  • 386
  • 2
  • 6
  • We did consider the approach you mentioned but we would like to keep the sizes dynamic since we don't want to use unnecessary S3 space. Note: We are calling the function directly via it's function URL. We are also putting the function URL behind Cloudfront which cache's the generated image nicely. Was hoping there was a way without `base64` – Izak Joubert Dec 22 '22 at 07:06