如何在 Python 中从 AWS 中的 lambda 函数返回二进制数据?

Posted

技术标签:

【中文标题】如何在 Python 中从 AWS 中的 lambda 函数返回二进制数据?【英文标题】:How to return binary data from lambda function in AWS in Python? 【发布时间】:2017-12-05 05:48:06 【问题描述】:

我无法让 python lambda 返回二进制数据。 node-template for thumbnail images 工作正常,但我无法让 python lambda 工作。以下是我的 lambda 中的相关行。 print("image_data " + image_64_encode) 行将 base64 编码的图像打印到日志中。

def lambda_handler(event, context):
    img_base64 = event.get('base64Image')
    if img_base64 is None:
        return respond(True, "No base64Image key")

    img = base64.decodestring(img_base64)
    name = uuid.uuid4()
    path = '/tmp/.png'.format(name)

    print("path " + path)

    image_result = open(path, 'wb')
    image_result.write(img)
    image_result.close()

    process_image(path)

    image_processed_path = '/tmp/-processed.png'.format(name)
    print("image_processed_path " + image_processed_path)
    image_processed = open(image_processed_path, 'rb')
    image_processed_data = image_processed.read()
    image_processed.close()
    image_64_encode = base64.encodestring(image_processed_data)

    print("image_data " + image_64_encode)


    return respond(False, image_64_encode)


def respond(err, res):
    return 
        'statusCode': '400' if err else '200',
        'body': res,
        'headers': 
            'Content-Type': 'image/png',
        ,
        'isBase64Encoded': 'true'
    

任何指向我做错了什么的指针?

【问题讨论】:

lambda 在哪里? 你有什么解决办法吗?我也有同样的问题。 【参考方案1】:

按照上述所有步骤对我的情况不起作用,因为对 content-type = */* 的二进制支持会将所有响应转换为二进制。

我的情况:

返回 json(文本)的多个 lambda 函数,只有一个返回二进制文件的 lambda。所有都启用了 lambda 代理

lambda 位于 API 网关中

API 网关位于 CloudFront 后面

提示: 我注意到 API Gateway -> Settings

中有一个重要信息

引用:

API Gateway 将查看 Content-TypeAccept HTTP 标头来决定如何处理正文。

这意味着 Content-Type 响应标头必须匹配 Accept 请求标头

解决方案:

    将 API 网关中的二进制媒体类型设置为您的 mime 类型:image/jpg

    在您的 HTTP 请求集中 Accept: image/jpg

    在您的 HTTP 响应集中 Content-Type: image/jpg


  "isBase64Encoded": True,
  "statusCode": 200,
  "headers":  "content-type": "image/jpg",
  "body":  base64.b64encode(content_bytes).decode("utf-8")

    接下来,我们必须告诉 CloudFront 接受请求中的“Accept”标头。因此,在 CloudFront 分发中,点击您的 API Gateway 实例(ID 可点击),一旦重定向到 CloudFront 实例,转到 Behaviour 选项卡,选择您的 API 的路径模式(例如:/api/* ) 并点击编辑按钮。

在新屏幕上,您必须将 Accept 标头添加到白名单。

注意 1:如果您有多种文件类型,则必须将它们全部添加到 API 网关设置中的 Binary Media Types

注意 2:对于那些来自 serverless 并希望在部署 lambdas 时设置二进制类型的用户,请查看此帖子:setting binary media types for API gateway

plugins:
  - serverless-apigw-binary

custom:
  apigwBinary:
    types:
- 'image/jpeg'

cloudfront 的 serverless.yml 文件应包含:

resources:
    WebAppCloudFrontDistribution:
      Type: AWS::CloudFront::Distribution
      Properties:
        DistributionConfig:
          ...
          CacheBehaviors:
            ...
            - 
              #API calls
              ...
              ForwardedValues:
                ...
                Headers:
                  - Authorization
                  - Accept

【讨论】:

感谢您提供此解决方案。对我来说,我忘记的部分是在请求中包含 Accept 标头。请务必检查!【参考方案2】:

我终于想通了。从 python lambda 返回二进制数据是可行的。

按照此处的说明进行操作: https://aws.amazon.com/blogs/compute/binary-support-for-api-integrations-with-amazon-api-gateway/

在创建新方法时,请务必选中“使用 Lambda 代理集成”。

还要确保您的 Python Lambda 响应返回 base64 编码的正文,将 isBase64Encoded 设置为 True,以及适当的内容类型:

import base64

def lambda_handler(event, context):
    # ...

    body = base64.b64encode(bin_data)

    return 'isBase64Encoded'   : True,
            'statusCode'        : 200,
            'headers'           :  'Content-Type': content_type ,
            'body'              : body 

那么:

对于您的每个路线/方法问题:

apigateway update-integration-response --rest-api-id <api-id> --resource-id <res-id> --http-method POST --status-code 200 --patch-operations "[\"op\" : \"replace\", \"path\" : \"/contentHandling\", \"value\" : \"CONVERT_TO_BINARY\"]"

在 AWS 控制台中。 和可以在 API Gateway 'breadcrumbs' 中看到 例如:

<api-id> = zdb7jsoey8
<res-id> = zy2b5g

那么: 您需要“部署 API”。根据我的发现,它仅在部署 API 后才起作用。

确保在部署之前设置“二进制媒体类型”。

提示: 不错的 AWS shell 终端:https://github.com/awslabs/aws-shell

pip install aws-shell

【讨论】:

很好的提示,但对我来说并不完全正确。你能告诉我你是如何编码base64_encoded_binary_data的吗? import base64 base64_encoded_binary_data=base64.b64encode(bin_data) 谢谢@user1495323!这很清楚。我认为我缺少的是“二进制媒体类型”实际上与客户端传入的 Accept 标头匹配,不是您的传出 Content-Type 标头,所以我需要添加 */* 到我的二进制媒体类型列表。 docs.aws.amazon.com/apigateway/latest/developerguide/… 因此,基本上,Lambda 函数将始终在 JSON 中接收/发送 base64 编码的数据。只有 API Gateway 会与客户端接收/发送二进制数据?【参考方案3】:

据我所知,Python 3 也是如此。我正在尝试返回二进制数据(字节)。它根本不起作用。

我也尝试使用 base-64 编码,但没有成功。

这与 API 网关和代理集成。

[更新]

我终于意识到如何做到这一点。我enabled binary support 输入*/* 然后返回这个:

return(
        "isBase64Encoded": True,
        "statusCode": 200,
        "headers": 
                "content-type": "image/jpg",
        ,  
        'body':  base64.b64encode(open('image.jpg', 'rb').read()).decode('utf-8')
)  

【讨论】:

这给了我一个 0 x 0 的图像,我不知道我做错了什么。 我首先检查 image.jpg 的内容并确保它对 Lambda 调用可用。 (示例中我没有做任何错误检查。)尝试 wget/curl 通过 API Gateway 捕获 Lambda 调用的结果。 谢谢,这对我有用!您知道为什么必须先将其编码为 base64,然后再将其解码为 utf-8 吗?在我看来,这是首先将其读取为字节,然后对其进行 base64 编码,然后对字节进行解码,然后再对 base64 进行解码。我原以为您必须切换最后两个操作才能不混合编码?【参考方案4】:

大约 6 个月前,我遇到了同样的问题。看起来虽然 API Gateway 中现在有二进制支持(以及 JS 中的示例),但 Python 2.7 Lambda 仍然不支持有效的二进制响应,不确定 Python 3.6。

由于 JSON 包装,Base64 编码响应存在问题。我在客户端编写了一个自定义 JS,手动从这个 JSON 中取出 base-64 图像,但这也是一个糟糕的解决方案。

将结果上传到 S3(在 CloudFront 后面)并将 301 返回到 CloudFront 似乎是一个很好的解决方法。最适合我。

【讨论】:

以上是关于如何在 Python 中从 AWS 中的 lambda 函数返回二进制数据?的主要内容,如果未能解决你的问题,请参考以下文章

在 aws lambda 中在 python 中从 ANSI 转换为 UTF-8

如何在 AWS 中从 Java 生成签名

如何在 local-exec 配置程序中从 terraform 继承 aws 凭据

如何在 aws 中从我的 mac 设置和登录 ***?

如何在我的 Spring Boot 应用程序中从 AWS 访问环境变量

在 AWS 中从 Lambda 启动 shell 脚本