Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correctly return binary file (image) from lambda through API gateway

Here is my lambda:

exports.handler = async (event) => {
    const hex = '89504e470d0a1a0a0000000d49484452000000a0000000a00800000000aea438f10000053e4944415478daed99bd6b2a4d1487effff4834504610988208204040b411002161616818010b0b0106e110b411041d258a4b0085858489a142902172e046c2c445208161224582c72dec29dd98fd911a2ee7a7939db654edc3c998fe79c39fea27ffcf9c5800cc8800cc8800cc8800cc8800cc8800cc8800cc8800cc8800cc8800cc8800cc8800cc8800cc8800cc8800cc8800cc880ff4bc09bab4cae58aed61a0f9dc741bfdb6adc554ab974d24c2d699b3693d962b5d668f506bd76b37e5b29e5b3c9d42759d99899ce97ef1aed6eb7ddac550a693356209a18f1ab6cb15a7fe8f43aad7ab5983163b9d30153d03c7f68630406de6811309a27eaa9a3d7a7037e34f3011c66b9f54df4524b2b7ff1b6bda1dde37dc1331a2fb5a6449fedbb8c7bd4283ccccfb20737936b2f457a6889d8bce20edccde48766b772343392bfbe1bcb7fc9e86cce7748ac41dc859159bb6343179f7b7c27d0db967b781ab3977c7ede53fcee22fceb0db565e0c5fb91fd60d7f7a6fa7e1156e7d6ccb3c448ec7cd39b1391b1776700006efd2f1ad827e9ec1e947b4a51c39b88fcf60c2f0120f9e5ffed2100d44310f522268ea4e50f89dd76b5758fbeaaab2e76447c194626f92d266aa2a8484446eed11a806a80f995a93e17e0a7f06185745398778dad0cc0508feada008ccf7072714d4c94f267df45e4d5196b05cfd400402da462e1afc0682aa1a21d293a131807126bf525d7aaa7ce57cd88fc955072c044b0bfbb776c477dc51b804268e59674e1c01fd9896458168e8901e6265856a3d000b7a68d91dd05da0d003ef63f3700f4d5372c0d20698557b0b6020e83cd9eb423fbccb13080d4960225d809b1a23e609abe609f1111dd03780a283a9280b10a1190aa5ad37c25ecc83d11cd0c2013b092633b1e1ee09bde340fa2cefbd41f8592b349c3ba345d6b4db33424fb14406ea77e78ea3165388003ad69ec420f88ad2a4109db3edaa390013709ad696682bdac91f126fe43c71c752f6e6a4de39c204d413a08a8afcf0f28e74935cd1f87ef46b37f7fe698e33a0b65ad6964c900fcd118e09ec2079ce84d234365dd9de12302402bad358d2c19d256701a2e5204804e52534d330cacfdf74f47b9f68505b88e694d234b0655d3561248599100d2bdde347dedc56a0ca017517ff0436f1a192a045ce662ab88001d9d28a6b9d3997af693dbfac980239d69668eaacb6a1a9e4606b8bdd298e6169a16d3260194283240a7a1355076a05cfeaa3f0d8f230494b59fd73415c058543cb5bffde48e72ccf180ce5abefa2ef64da7cb50f3761e7a1425e07b9069ca407ce59c7163e13edcb175a48094534df30ea0ed2e191aee3e529da2057c524d7303985f44b4cbcadadf9586a711037e27fca67903f0e82d191eec349cd254b061023aedcc81935eec5e822c19eceed65873870a17700eaf695e000cfd254357ac7dda8a1c5076556dd3e4816bcbdf6530bfedfcd7a7e8015f3ca6997896b1e55eff26105f5f00709771996697f3545832d3a42cda248e76cc6980f4e8fa7a64e42bb0449701437a3ade312702aee3d23456d6575fc9ba2b6be58e77cc8980ce340d9e951ba5ec32344e70cca98053394d19df379d9e2ec3f18e3915904aae2fa7e7fa589f2e053876201a5a0b9de0989301ada48458eaeb9d065d0c903a02a2a5c69e832aeba80157b690cd8055b452071a4951018a7b70ff80c85f2e0ab8b749721b14db980090de5d1490f208feca465e4e1fe9b2804300598d88573120fe7561c0ad79e046de38d131e700a4ee81526061c6e71707a4e58153f0bda1cb0386fb30200332200332200332200332200332200332200332200332200332200332200332200332e03ffafc0761478d7dbc33444a0000000049454e44ae426082'

    const image = Buffer.from(hex, 'hex');

    const result = /* "data:image/png;base64," + */ image.toString('base64');

    const response = {
        statusCode: 200,
        headers: {
            'Content-Type': 'image/png'
        },
        body: result,
        isBase64Encoded: true
  }


    return response;
};

Which is returned fine as JSON.

Next, in API Gateway, GET - Integration Response:

I set following through AWS Console: Content handling: Convert to binary (if needed)

Header Mappings: Content-Type: 'image/png'

Mapping Template: image/png. Currently I use:

{
    "base64Image": $input.path('$.body')
}

Finally, in GET - Method Response:

Response Headers for 200: Name: Content-Type

Response Body for 200: Content-Type: image/png and Model: Empty

Binary Media Types is enabled with image/png.

Unfortunately, Method Execution fails:

Mon Sep 23 09:52:37 UTC 2019 : Endpoint response body before transformations: {"statusCode":200,"headers":{"Content-Type":"image/png"},"body":"iVBORw0KGgoAAAANSUhEUgAAAKAAAACgCAAAAACupDjxAAAFPklEQVR42u2ZvWsqTRSH7//0g0UEYQmIIIIEBAtBEAIWFhaBgBCwsBBuEQtBEEHSWKSwCFhYSJoUKQIXLgRsLERSCBYSJFgsct7CndmP2RGi7np5OdtlTtw8mY/nnDn+on/8+cWADMiADMiADMiADMiADMiADMiADMiADMiADMiADMiADMiADMiADMiADMiADMiADMiA/0vAm6tMrliu1hoPncdBv9tq3FVKuXTSTC1pmzaT2WK11mj1Br12s35bKeWzydQnWdmYmc6X7xrtbrfdrFUKaTNWIJoY8atssVp/6PQ6rXq1mDFjudMBU9A8f2hjBAbeaBEwmifqqaPXpwN+NPMBHGa59U30Uksrf/G2vaHd433BMxovtaZEn+27jHvUKDzMz7IHN5NrL0V6aInYvOIO3M3kh2a3cjQzkr++G8t/yehszndIrEHchZFZu2NDF597fCfQ25Z7eBqzl3x+3lP87iL86w21ZeDF+5H9YNf3pvp+EVbn1syzxEjsfNObE5Gxd2cAAG79LxrYJ+nsHpR7SlHDm4j89gwvASD55f/tIQDUQxD1IiaOpOUPid12tXWPvqqrLnZEfBlGJvktJmqiqEhERu7RGoBqgPmVqT4X4KfwYYV0U5h3ja0MwFCP6toAjM9wcnFNTJTyZ99F5NUZawXP1ABALaRi4a/AaCqhoh0pOhMYBxJr9SXXqqfOV82I/JVQcsBEsL+7d2xHfcUbgEJo5ZZ04cAf2YlkWBaOiQHmJlhWo9AAt6aNkd0F2g0APvY/NwD01TcsDSBphVewtgIOg82etCP7zLEwgNSWAiXYCbGiPmCavmCfERHdA3gKKDqSgLEKEZCqWtN8JezIPRHN [TRUNCATED]
Mon Sep 23 09:52:37 UTC 2019 : Execution failed due to configuration error: Unable to transform response
Mon Sep 23 09:52:37 UTC 2019 : Method completed with status: 500

What's wrong with my configuration? Did I miss something and where?

What I expect to get is image/png returned as binary file.

like image 880
iaforek Avatar asked Sep 23 '19 09:09

iaforek


2 Answers

If you are using Express and Serverless this worked for me:

Install the serverless-apigw-binary plugin:

npm install --save-dev serverless-apigw-binary

Be sure to have serverless-http installed:

npm i serverless-http

The answer here helped me to return a base-64 image in Express:

Express send base-64 encoded png-image

Here is my app.js file. Note that you must reference the binaries in module.exports.handler:

const serverless = require("serverless-http");
const express = require("express");
const app = express();

app.get('/', function (req, res){
   var dataUrl = "data:image/png;base64,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";
   var dataUrlEdit = dataUrl.replace(/data:image\/png;base64,/, '');
   var img = Buffer.from(dataUrlEdit, 'base64');
   res.writeHead(200, {
     'Content-Type': 'image/png',
     'Content-Length': img.length
   });
   res.end(img);
})

module.exports.handler = serverless(app, {
    binary: ['image/png', 'image/gif']
  });

Here is my serverless.yml file:

service: your-app

provider:
  name: aws
  runtime: nodejs10.x
  stage: dev
  region: us-east-1

functions:
  app:
    handler: app.handler
    events:
      - http: ANY /
      - http: 'ANY {proxy+}'

plugins:
  - serverless-apigw-binary

custom:
  apigwBinary:
    types:
      - '*/*' 

Thanks to the answer from tomyates here:

https://github.com/dougmoscrop/serverless-http/issues/34

like image 89
Mr. J Avatar answered Oct 09 '22 20:10

Mr. J


There are three main steps required:

  1. Lambda must return the image as base64 encoded string: return image.toString('base64')
  2. In API Gateway > Method Execution > Integration Request > Mapping Templates - add Content-Type: image/png
  3. In API Settings enable Binary Media Types and set it as image/png

Deploy API and in POSTMAN call the endpoint URL with headers: Accept: image/png and Content-Type: image/png.

That's it. This will return the image correctly as image!

If anyone needs more details, I've created a full tutorial that goes through each step: https://medium.com/@iaforek/how-to-configure-binary-support-in-api-gateway-the-easy-and-working-way-ac2d68face36

What's important, you don't need to use CONVERT_TO_BINARY passthrough as other blog posts suggest.

like image 21
iaforek Avatar answered Oct 09 '22 19:10

iaforek