AWS API网关和Lambda返回图像

25

假设我有这个HTML:

<img src="http://example.com/pic"/>

我想做的是让example.com/pic映射到一个AWS API Gateway端点。
然后,该端点将调用一个lambda函数。
该lambda函数将从s3存储桶中读取一张随机图片并返回。
因此,我的目标是使用标准的HTML图像标签,并通过lambda中的一些决策代码来获取来自s3存储桶的图像。
我知道你可以直接使用s3来提供静态内容(因此需要lambda来决定要返回的图像)。我也知道我可以在lambda中进行一些操作,比如对响应进行b64编码,然后在客户端处理,但我希望使用标准的HTML IMG标签。
这种可能吗?
我尝试使用ResponseStreamHandler(Java SDK)作为lambda的返回类型,并返回图像的字节数组,还添加了API Gateway配置以不将输出映射到JSON,但似乎没有任何效果!
6个回答

48

看起来 AWS 已经简化了这个过程,所以很多答案已经过时或过于复杂。

以下是我在 2018 年 6 月的方法,让 Lambda 通过 API 网关返回图像:

1)在 API Gateway 中,为您的 API 启用 Use Lambda Proxy integration(此设置位于集成请求部分,您需要将类型设置为 Lambda)。

2)在 API Gateway 中,选择您的 API,然后单击 Settings。在 Binary Media Types 中添加 */*。(注意:我尝试添加简单的 'image/jpeg',但似乎需要 */* 才能使所有内容正常工作)

3)一定要部署您的 API,否则您的更改不会生效。(在 API Gateway 中,选择您的 API,然后选择操作 > 部署 API)。

4)在您的 Lambda 代码中,以 Base64 编码返回您的图像(以下是 C# 代码示例):

    // set the Content-type header
    // set to whichever image type you're returning
    var headersDic = new Dictionary<string, string>();
    headersDic.Add("Content-type", "image/jpeg");

    // return the response object that APIGateway requires
    return new APIGatewayProxyResponse
    {
        StatusCode = 200,
        Headers = headersDic,
        // return the image in Base64 encoding
        Body = Convert.ToBase64String(...your image data...),
        IsBase64Encoded = true
    };

完成。

如果您已经设置了API不需要身份验证,只需将API链接输入到浏览器中,它就会显示图像。或者将API链接放入IMG标签中。例如:<img src="https://asdf.execute-api.us-east-1.amazonaws.com/live/myapi" />

注意:即使在第2步中设置了Binary Media Types*/*,如果您的Lambda返回的是文本,API Gateway仍然会返回文本。


5
非常感谢。我错过了这一步。 3)务必部署您的API,否则更改将不会生效。(在API网关中,选择您的API,然后选择操作>部署API)。 - beehuang
4
之所以需要 */* 是因为 APIG 要求接受头匹配,这很愚蠢。 因此客户端必须发送 Accept: image/jpeg 以使二进制解码发生。因为 Chrome(所有浏览器?)发送 Accept: */*,所以这使得它起作用。 - Cory Mawhorter
3
感谢您提供*/*,以及让我们了解每次更改内容后需要部署API的信息! - Marcin
7
isBase64Encoded设置为true似乎是使返回二进制数据的关键。这种魔法似乎就是这样实现的。 - Jochem Schulenklopper
1
@JochemSchulenklopper 所有的内容都需要返回一张图片。 - Doug S
显示剩余5条评论

9
幸运的是,现在AWS API Gateway支持二进制数据,但是您还需要通过CLI更新资源方法,因为它尚未在控制台中实现。这是您需要做的:
  1. 在方法响应中
    将HTTP 200状态响应头中的Content-Type设置为image/jpeg
  2. 在集成响应中
    在头映射中将Content-Type设置为'image/jpeg'。请注意引号!
  3. 使用AWS CLI,在您的集成响应上将contentHandling属性设置为CONVERT_TO_BINARY
在这个非常好的逐步指南中检查整个过程:https://dev59.com/SVgR5IYBdhLWcg3wXMPe#41434295

7
我遇到了类似的问题。如上所述,目前无法直接从API Gateway端点以二进制格式返回图像,这是浏览器正确显示图像所必需的。
但是,我通过让API Gateway返回 302重定向,指向S3中的正确文件来解决了这个问题。您可以让Lambda函数返回文件的URL,稍后将其映射到API Gateway中的Location头。浏览器将跟随重定向并正确显示图像。
实现重定向的方法有几种,但我按如下方式执行:
  • Lambda returns an object with the target image like so:

    function handler(event, context) {
         context.succeed({ 
             location: "https://[bucket-name].s3-eu-west-1.amazonaws.com/myimage.png" });
         });
    }
    
  • Remove the normal '200' Method Response Status from The Integration Response in API Gateway. Replace it with a '302' response status and add the 'Location' header mapped to value 'integration.response.body.location'

  • Add the 302 status to the Method Response as well


1

为了明确起见,客户端会发出两个不同的请求:

  1. 第一个请求是获取HTML(包括图片URL)。
  2. 第二个请求是从URL中获取图像数据。

换句话说,图像数据不会内联在HTML中。

基于这个知识,您可以像您建议的那样,在API网关后面使用Lambda。 Lambda实现可以具有一些逻辑,以确定存储在S3中的图像的URL。但是,Lambda返回JSON数据而不是HTML(有解决方法,例如return the html in a variable),这使事情变得更加棘手,特别是对于大型HTML页面。

我建议采用稍微不同的方法,因为仅接收图像标记将无法让您走得太远。我假设您将通过使用JavaScript将图像标记内联到HTML文档中。然后,您可能会让API Gateway / Lambda请求返回一个带有图像URL的JSON文档,并让JavaScript更新具有新URL的现有图像标记或为您生成标记。


0

目前不可能实现,因为您无法通过AWS API Gateway返回二进制数据。

要使此功能正常工作,Lambda函数需要将图像数据作为二进制块返回,并提供一些元信息,例如图像内容类型。然后,AWS API Gateway需要能够将其映射到HTTP响应。例如:

Lambda返回: { contentType: 'image/png', image: "encoded binary data" }

然后API网关需要将contentType映射到响应的“content-type”标头,并使用正确的编码和长度将图像数据放在响应的正文中。

不幸的是,它现在还没有这样做。它只将文本编码(如application/json或application/xml)映射为响应类型(毕竟它是为API设计的)。

您可以使用ElasticBeanstalk轻松实现此目标,在那里您对http响应有更多控制权。


自2016年11月起,AWS API Gateway开始支持返回二进制数据。请参见:API Gateway现在支持二进制数据 - Yagiz Degirmenci

0

我通过重新考虑我的设计解决了这个问题。本质思想是Lambda是很好的计算单元,但不适合作为文件服务器。

我做出了以下更改

  • 客户端 > APIGW > Lambda > 图片

变成了

  • 客户端 > APIGW > Lambda > 签名URL
  • 然后 客户端(签名URL) > 图片

由于我的客户端是基于Web的,所以在这个转变之后一切都变得更容易了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接