如何在 Lambda 和 vice-versa 中处理来自 API 网关的二进制数据?

How to process binary data from API Gateway in Lambda and vice-versa?

我目前有以下设置: 1. S3 桶 2. API 具有 GET/POST 资源的网关 3. 两个Lambda函数(一个从s3取数据,一个存数据到s3)

例如 GET 资源,我传递了一个关键参数,它在 lambda 函数中用于从 s3 存储桶中获取 object。然后我想 return 检索到 object 到 api 网关和客户端作为二进制文件。

在 POST 资源中,我想发送二进制负载,例如:pdf 或 zip 文件,将其保存到 s3 和 return 生成的密钥。

所以一方面我希望我的 api 网关和 lambda 能够在 GET 请求中 return 二进制数据,另一方面我希望它在 POST 中接受二进制有效载荷] 请求。

在 api 网关设置中,我已将 Binary-Media-Types 设置为 application/octet-stream。我还激活了 lambda 代理集成。

我的问题是:如何处理 api gateway/lambda 中的二进制数据?

我试着摆弄 Headers 和 Content-Types 但或多或少我不知道我在做什么 :(

示例 store-luggage 用于在 S3 中存储来自 POST 请求的数据的 lambda

import boto3
import json
import uuid
import logging
from botocore.exceptions import ClientError

def lambda_handler(event, context):

    #data = <binary data from POST request>?

    logger = logging.getLogger()
    logger.setLevel(logging.INFO)

    bucket = 'bucket1'

    uniqueid = str(uuid.uuid4())
    logger.info("Generated UUID: " + uniqueid)

    s3 = boto3.resource('s3')

    logger.info("Trying to save file '" + uniqueid + "' to bucket '" + bucket + "'")

    savedObj = None

    try:
        savedObj = s3.Bucket(bucket).put_object(Key=uniqueid, Body=<here should be my binary payload from POST request>)
    except ClientError as e:
        logger.error("Saving of object has failed: " + str(e.response['Error']['Message']))

    if savedObj is None:
        return {
            'statusCode': 500,
            'body': json.dumps({
                'message': 'Saving of object has failed!'
            })
        }

    return {
        'statusCode': 200,
        'body': json.dumps({
            'token': uniqueid
        })
    }

get-luggage 用于通过给定密钥从 s3 检索 object 的 lambda 和响应中的 return 二进制负载

import boto3
import json
from pprint import pprint
from botocore.exceptions import ClientError
import logging

def lambda_handler(event, context):

    logger = logging.getLogger()
    logger.setLevel(logging.INFO)

    bucket = 'bucket1'
    token = event["queryStringParameters"]["token"]

    s3 = boto3.client('s3')

    objectFromS3 = None

    try:
        objectFromS3 = s3.get_object(Bucket=bucket, Key=token)
    except ClientError as ex:
        if ex.response['Error']['Code'] == 'NoSuchKey':
            logger.info('No object found - returning empty')
        else:
            raise ex

    if objectFromS3 is None:
        return {
            'statusCode': 404,
            'body': json.dumps({
                'message': 'Cannot retrieve object from storage!'
            })
        }

    pprint(objectFromS3)
    #ContentType application/octet-stream

    return {
        'statusCode': 200,
        'body' : <binary data from retrieved s3 object here?>,
        'headers': {
            'content-type': 'application/octet-stream'
        },
        'isBase64Encoded': True
    }

我希望在 GET 响应中检索二进制负载,并希望 api 网关将二进制负载从 POST 请求传递到 lambda,但是 none 这是通过我的东西实现的试过了。

我希望我对我的问题的描述足够好,如果需要任何澄清,请告诉我。

最良好的祝愿, aws 新手

首先,您需要在 API 网关设置中配置您希望将其视为二进制数据的 MIME 类型。

Settings -> Binary Media Types.

指定要用作二进制数据的确切 MIME 类型 application/pdf、application/zip 等。application/octet-stream 表示确切类型未知的通用数据。

在您的 Http 请求中,应该有 Accept header 指示负载中包含哪种 MIME 类型。

  • 接受:application/pdf

  • 接受:application/zip

由于 AWS Lambda 以 base64 编码处理请求和响应,您应该解码 POST 请求 body 并在上传到 S3 存储桶之前获取原始二进制内容。

从 S3 存储桶中获取数据后,lambda 在将二进制数据发送到 API 网关之前对其进行编码。作为响应,您应该将 isBase64Encoded 标志的值设置为 True。然后API网关对编码后的数据进行Base64解码再发送给客户端如果Http请求包含相应的Acceptheader.

get-luggage:

如果您的文件已经在 S3 中,您可以通过在 API-Gateway headers 上添加 Location 属性将用户重定向到 S3 中的文件 link,减少网关数据流量。

示例:

exports.handler = (event, context, callback) => {
  return callback(null, {
    statusCode: 301,
    headers: {
      Location: 'https://<...S3...>',
    }
  });
}

在方法请求 > HTTP 请求 Headers 提交 "Accept: application/pdf" 是不可能的,因为它 returns 错误,"Invalid patch path /requestParameters/method.request.header.Accept: application/pdf."

对于我自己,我有以下 Lambda:

import boto3
import base64

def lambda_handler(event, context):
    s3 = boto3.client("s3")

    fileObj = s3.get_object(Bucket="mytestbucket", Key="sample.pdf")
    file_content = fileObj["Body"].read()

    return {
        "statusCode": 200,
        "headers": {"Content-Type":"application/pdf"},
        "body": base64.b64encode(file_content),
        "isBase64Encoded": True
        }

但是响应 body 是 base64 编码的,而不是 sample.pdf 文件本身。是的,我在 API 网关控制台的设置中将 */* 设置为二进制媒体类型。有什么建议我可能遗漏了什么吗?