【问题标题】:Returning binary file (pdf) from an AWS Lambda via AWS API Gateway通过 AWS API Gateway 从 AWS Lambda 返回二进制文件 (pdf)
【发布时间】:2021-07-29 23:53:13
【问题描述】:

有几个类似的问题,但到目前为止,没有一个答案对我有用。

我在由 Serverless 提供支持的 AWS API 网关后面有一个 AWS Lambda 函数,Lambda 应该通过以下方式返回 PDF 文档:

let responseObj = {
      statusCode: 200,
      isBase64Encoded: true,
      headers: {
        'Content-type': 'application/pdf',
        // 'accept-ranges': 'bytes',
        'Content-Disposition': 'attachment; filename=' + pdfName + '.pdf'
      },
      body: pdfBuffer && pdfBuffer.toString('base64')
    }
    return responseObj;

当我对 pdfBuffer 的 AWS CloudWatch 执行 console.log() 时(在 base64 之前,它确实看起来像 PDF 数据:

%PDF-1.4
%����
1 0 obj
<</Creator (Chromium)
/Producer (Skia/PDF m90)
...

然而,当我看邮递员时,我看到的是我的身体:

JVBERi0xLjQKJdPr6eEKMSAwIG9iago8PC9DcmVhdG9yIChDaHJvbWl1bSkKL1Byb2R1Y2VyIChTa2lhL1...

所以它显然没有返回二进制文件(我的 pdf)。

查看 API Gateway,建议您将 Binary Media Types 设置为包含 */*

现在,我的 API 网关有两个端点,当我将其设置为 */* 时,PDF 服务端点确实正确地为我的 PDF 提供服务,但是我有另一个端点接收 JSON 正文,当 @987654334 @ 设置在 Binary Media Types 下,它会错误地/base64 对 JSON 输入进行编码,从而使我的 CSV 端点无用。

Binary Media Types 设置为包含 application/pdf 允许我的 CSV 服务端点工作,但我的 PDF 端点恢复为提供垃圾数据,即使手动设置 Accepts 邮递员中的标头到application/pdf

所以将 Binary Media Types 保留为 application/pdf,我转向 API Gateway UI 设置中的资源:

在这里我有点不确定要编辑哪个。似乎我在侧边栏中有两个选项,一个用于 GET,一个用于 OPTIONS:

OPTIONS - Method Execution 允许我编辑 Integration Response,而 GET - Method Execution 不能。

当我编辑 Integration Response 选项并将 Content Handling 设置为 Convert to binary (if needed) 时,通过 Postman 返回给我的内容似乎没有变化。

必须有一个步骤或我缺少的东西。将 Binary Media Types 设置为包含 */* 似乎是一个错误的答案。必须有一种方法允许某些端点返回二进制数据(如 pdf 文件),同时允许其他端点返回或接受非二进制数据。

【问题讨论】:

  • 你有什么运气吗?我在尝试从 Lambda/API 网关提供 PDF 文件时遇到了同样的问题
  • @GerardvandenBosch 我不得不走上正轨才能让它工作,即使那样它也不能完全按照我的意愿去做。您需要删除代理 lambda 集成,然后您可以编辑集成响应。但是你只能传回base64编码的二进制文件(没有头文件),否则如果你尝试包含头文件等,它似乎是二进制文件格式错误。
  • @GerardvandenBosch 也更改为非代理 lambda 集成将影响您将数据传递给 lambda 的方式。
  • @GerardvandenBosch 看到我的回答。如果您找到更好的解决方案,请添加到它(因为它是一个 wiki)。

标签: amazon-web-services aws-lambda aws-api-gateway binaryfiles


【解决方案1】:

我将添加一个答案作为 wiki。我认为这是会改变的,当然截图会随着时间而改变,应该更新,而不是添加新的答案。

要启用 lambda 的二进制输出,您必须在 Integration Request 页面上禁用“Use Lambda Proxy Integration”:

通过更改此设置,它将消除您通常期望的 Lambda 事件对象(易于访问的路径参数、正文参数等),因此您需要在同一页面上更改映射模板集成请求。

出于我自己的需要,我将 Request body passthrough 设置为 当没有定义模板时(推荐),其 Content-Type 为:application /pdf 和以下模板:

##  See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html
##  This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload
#set($allParams = $input.params())
{
"body-json" : $input.json('$'),
"params" : {
#foreach($type in $allParams.keySet())
    #set($params = $allParams.get($type))
"$type" : {
    #foreach($paramName in $params.keySet())
    "$paramName" : "$util.escapeJavaScript($params.get($paramName))"
        #if($foreach.hasNext),#end
    #end
}
    #if($foreach.hasNext),#end
#end
},
"stage-variables" : {
#foreach($key in $stageVariables.keySet())
"$key" : "$util.escapeJavaScript($stageVariables.get($key))"
    #if($foreach.hasNext),#end
#end
},
"requestContext" : {
    "authorizer" : {
#foreach($key in $context.authorizer.keySet())
"$key" : "$util.escapeJavaScript($context.authorizer.get($key))"
    #if($foreach.hasNext),#end
#end
    },
    "account-id" : "$context.identity.accountId",
    "api-id" : "$context.apiId",
    "api-key" : "$context.identity.apiKey",
    "authorizer-principal-id" : "$context.authorizer.principalId",
    "caller" : "$context.identity.caller",
    "cognito-authentication-provider" : "$context.identity.cognitoAuthenticationProvider",
    "cognito-authentication-type" : "$context.identity.cognitoAuthenticationType",
    "cognito-identity-id" : "$context.identity.cognitoIdentityId",
    "cognito-identity-pool-id" : "$context.identity.cognitoIdentityPoolId",
    "http-method" : "$context.httpMethod",
    "stage" : "$context.stage",
    "source-ip" : "$context.identity.sourceIp",
    "user" : "$context.identity.user",
    "user-agent" : "$context.identity.userAgent",
    "user-arn" : "$context.identity.userArn",
    "request-id" : "$context.requestId",
    "resource-id" : "$context.resourceId",
    "resource-path" : "$context.resourcePath"
    }
}

这现在应该通过您在 Lambda 的 event 参数中所期望的内容。您可能需要更改映射模板中的名称以满足您的需求,或者在您的 Lambda 代码中更改名称。

您的端点仍然无法正确输出 PDF。

我们应该为方法响应添加一个响应,出于我自己的目的,我添加了一个200响应。我添加了一个“Content-Type”的Response Header和一个“application/pdf”的Response Body”的模型

此时,如果您的 Lambda 正在输出:return pdfBuffer.toString('base64'),您应该开始在 curl/postman 请求中看到正确的标头和 base64 正文:

curl -i --output - --location --request GET 'https://example.com/document-generation/soa/pdf/unitresult/1cc1eece-cf4e-e811-80e5-00155d210d92/2a174f81-a055-e511-80c2-00155d220a0c' \
--header 'Content-Type: application/pdf' \
--header 'Accept: application/pdf' \
--header 'Authorization: abc123
HTTP/2 200
content-type: application/pdf
content-length: 32502
date: Wed, 26 May 2021 09:58:15 GMT
x-amzn-requestid: blah blah
x-amz-apigw-id: anonymous
x-amzn-trace-id: all kinds of things
via: other stuff
x-amz-cf-pop: letters
x-cache: Miss from cloudfront
x-amz-cf-pop: letters
x-amz-cf-id: stuff

"JVBERi0xLjQKJdPr6..."

我们现在需要查看 Integration Response 设置页面,该页面现在由于禁用了Use Lambda Proxy Integration而启用。

200 方法响应状态应该已经设置好了,展开它并将“内容处理”设置为转换为二进制(如果需要)。展开 Header Mappings 并添加一个“Content-Type”的Response header,其Mapping value为“'应用程序/pdf'".

如果您重新运行 curl 或 Postman 请求,您现在应该可以正确收到 PDF。

如果您需要从不同域的网站访问 PDF,则需要允许 CORS。为此,您需要向 PDF 服务资源添加一个 OPTIONS 方法。

在新 OPTIONS 方法的集成请求中,您需要将其设置为 Mock。

在您的 OPTIONS 方法的方法响应中,您需要添加:

  • 访问控制允许标头
  • 访问控制允许方法
  • 访问控制允许来源

到 200 的响应标头。

在您的 OPTIONS 方法的集成响应中,您需要添加以下标头映射:

  • 响应标头:访问控制允许标头

  • 映射值:'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Amz-User-Agent'

  • 响应标头:访问控制允许方法

  • 映射值:'OPTIONS, GET'

  • 响应标头:Access-Control-Allow-Origin

  • 映射值:'*'

如果您的 PDF 端点使用的不是 GET。您将需要更改 Access-Control-Allow-Methods 的映射值以匹配方法类型,例如'OPTIONS, POST' 如果您的 pdf 是通过 POST 方法提供的。

【讨论】:

    猜你喜欢
    • 2019-02-08
    • 1970-01-01
    • 1970-01-01
    • 2017-06-30
    • 2018-06-16
    • 1970-01-01
    • 2020-04-24
    • 2015-10-15
    • 2017-04-13
    相关资源
    最近更新 更多