AWS Lambda无法返回PDF文件

29
我使用serverless创建了一个lambda函数。这个函数通过API Gateway在GET请求时触发,并应该从缓冲区返回一个pdf文件。我正在使用html-pdf来创建缓冲区,并尝试使用以下命令返回pdf文件。
  let response = {
    statusCode: 200,
    headers: {'Content-type' : 'application/pdf'},
    body: buffer.toString('base64'),
    isBase64Encoded : true,
  };
  return callback(null, response);

但是浏览器无法加载PDF文件,所以我不知道如何直接将PDF文件返回给浏览器。找不到解决办法。

1
我对Base64编码很好奇。这是必要的吗?也许这就是问题所在?我会以二进制格式返回数据。 - C-Otto
我尝试了一些变化(也尝试了不使用bas64编码)。建议来自以下链接:https://github.com/serverless/serverless/issues/2797 - sami_analyst
6个回答

41

好的,我找到了答案。 我的响应对象中的设置很好,我只需要手动更改API Gateway中的设置,才能使其在浏览器中工作。我在API Gateway控制台的二进制设置下添加了"*/*"来支持二进制媒体类型。

API 网关

  1. 登录您的控制台
  2. 选择您的 API
  3. 在下拉菜单中选择二进制支持
  4. 编辑二进制媒体类型并添加"*/*"

前端

在新标签页中打开 API URL (target="_blank")。可能是浏览器正在处理已编码的 Base64 响应,在我的情况下使用 Chrome 浏览器,浏览器会在新标签页中打开 PDF,正如我想要的那样。


2
你能分享一下: 1)API网关上做了哪些确切的更改? 2)你是如何处理客户端上的base64编码响应字符串的? - Shuki
@sami_analyst 这个对我有用,谢谢。很奇怪的是当我上传PDF时 'application/pdf' 不起作用。 - Christopher Grigg
2
我将二进制媒体类型设置为“application/pdf”,并确保API Gateway - Method Response包括一个HTTP状态 "200",其中响应正文Content-type为“application/pdf”(空模型)。此外,还包括一个HTTP状态 "500",其响应正文Content-type为"text/plain"(空模型),以处理出现错误的情况。 - jpblancoder
@FrederikNygaardHavlundSvend 对我来说不起作用,您能告诉我可能出了什么问题吗?我从 Lambda 集成获取一个 base64 字符串,然后将 application/pdf 添加到方法响应定义中以获取 200 状态,但我仍然得到 text/plain 响应。 - Haris Mehmood
2
@HarisMehmood 我已将 wkhtmltopdf 分离到它自己的 Lambda 函数中,并有一个 dotnet core webapi 调用该 Lambda 函数以获取 base64 编码的字符串。然后,我对该字符串进行 UTF8 解码为 byte[] 并从 WebApi 返回该文件。我的问题是,WebApi 的响应是无效的 pdf 文件。在 apiGateway 的设置页面上添加以下二进制媒体类型:“*/*”,并添加具有“application/pdf”和“Empty”模型的 200 OK 方法响应即可解决此问题。然后重新部署 API Gateway。请告诉我这是否解决了您的问题。 - Frederik Nygaard Svendsen
显示剩余7条评论

18
在花费了数小时后,我发现如果将内容处理(Content handling)设置为转换为二进制(Convert to binary)(CONVERT_TO_BINARY),则整个响应都必须是base64编码的,否则我会收到错误:无法对主体进行base64解码
因此,我的响应现在看起来像这样: callback(null, buffer.toString('base64')); 集成响应(Integration response):

enter image description here

方法响应:

enter image description here

二进制媒体类型:

enter image description here


2
如果您选择使用SAM,请添加以下行:Globals: Api: BinaryMediaTypes: - application~1pdf - Chinh Nguyen
您可以在无服务器框架中添加二进制媒体类型,您可以在此处查看二进制媒体类型响应 - Alex Montoya

7

如果你有一个巨大的PDF文件,那么Lambda返回它需要很长时间,并且在Lambda中,你需要按每100毫秒计费。

我建议先将其保存到S3,然后让Lambda返回S3 URL供客户端下载。


上传到S3的过程不是至少需要同样多的时间吗?谢谢您的建议,但我必须直接下载文件。 - sami_analyst
12
还要注意响应内容的大小限制,不要超过6MB。 - TFischer
对于受保护的文件,Lambda 如何知道是哪个用户上传了该文件?在我从 Lambda 上传并尝试从我的应用程序访问它时,我总是收到 404 无密钥错误。 - conor909
@conor909 请尝试创建一个新的问题。 - Noel Llevares

1

我遇到了类似的问题,PDF以base64格式下载,并且这个问题是在更改serverless.yml文件后出现的:

binaryMediaTypes:
      - '*/*'

为了

binaryMediaTypes:
      - 'application/pdf'
      - '....other media types'

问题出在AWS实现这个功能的方式上。从这里的AWS文档可以看到:
当请求头Accept包含多个媒体类型时,API Gateway仅使用第一个Accept媒体类型。如果你无法控制Accept媒体类型的顺序,且二进制内容的媒体类型不是列表中的第一个,则需要将二进制媒体类型添加到API的binaryMediaTypes列表中。API Gateway会将此列表中的所有内容类型视为二进制。
简而言之,如果请求头Accept中的第一个媒体类型不在binaryMediaTypes列表中,那么你将得到Base64编码。
我检查了浏览器中的请求头,发现Accept头中的第一个媒体类型是text/html,所以在更改设置后使其工作。
binaryMediaTypes:
          - 'application/pdf'
          - '....other media types'
          - 'text/html'

希望这篇文章能够帮助到遇到相同问题的人。

0

不要再做这些了。最好在您的serverless.yaml文件中使用serverless-apigw-binary插件。

添加

plugins:
- serverless-apigw-binary

custom:
   apigwBinary:
    types:
      - "application/pdf"

希望能对某人有所帮助。


0
以上解决方案仅适用于特定的内容类型。您无法更改内容类型。 只需按照以下两个步骤解决多个内容类型问题即可。
  1. 单击使用Lambda代理集成的复选框

API网关--> API-->方法-->集成请求

enter image description here

enter image description here

  • 将您的响应创建为

        let response = {
    
          statusCode: 200,
          headers: {
            'Content-type': 'application/pdf',//您可以更改任何内容类型
            'content-disposition': 'attachment; filename=test.pdf' // 成功的关键
          },
          body: buffer.toString('base64'),
          isBase64Encoded: true
        };
        return response;
    
  • 注意* - 这不是安全的


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