sami_analyst
sami_analyst

Reputation: 1839

AWS Lambda fails to return PDF file

I have created a lambda function using serverless. This function is fired via API Gateway on a GET request and should return a pdf file from a buffer. I'm using html-pdf to create the buffer and trying to return the pdf file with the following command

  let response = {
    statusCode: 200,
    headers: {'Content-type' : 'application/pdf'},
    body: buffer.toString('base64'),
    isBase64Encoded : true,
  };
  return callback(null, response);

but the browser is just failing to load the pdf, so I don't know exactly how to return the pdf file directly to the browser. Could'nt find a solution for that.

Upvotes: 34

Views: 55581

Answers (6)

Farukh Khan
Farukh Khan

Reputation: 305

Instead of doing all this. It's better to use serverless-apigw-binary plugin in your serverless.yaml file.

Add

plugins:
- serverless-apigw-binary

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

Hope that will help someone.

Upvotes: 0

Goca
Goca

Reputation: 1863

I was having a similar issue where pdf where downloaded as base64 and started happening when changed the serverles.yml file from:

binaryMediaTypes:
      - '*/*'

to

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

The issue is because the way AWS implemented this feature. From aws documentation here:

When a request contains multiple media types in its Accept header, API Gateway honors only the first Accept media type. If you can't control the order of the Accept media types and the media type of your binary content isn't the first in the list, add the first Accept media type in the binaryMediaTypes list of your API. API Gateway handles all content types in this list as binary.

Basically if the first media type contained in the accept request header is not in your list in binaryMediaTypes then you will get base64 back.

I checked the request in the browser and the first media type in the accept header was text/html so I got it working after changing my settings to:

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

Hope this helps anyone with the same issue.

Upvotes: 2

sami_analyst
sami_analyst

Reputation: 1839

well, I found the answer. The settings in my response object are fine, I just had to manually change the settings in API Gateway for this to work in the browser. I have added "*/*" to binary media types under the binary settings in API Gateway console

API GATEWAY

  1. just log into your console
  2. choose your api
  3. click on binary support in the dropdown
  4. edit binary media type and add "*/*"

FRONTEND

opening the api url in new tab (target="_blank"). Probably the browser is handling the encoded base 64 response, In my case with chrome, the browser just opens the pdf in a new tab exactly like I want it to do.

Upvotes: 41

Prashant Tapase
Prashant Tapase

Reputation: 2147

Above solution is only for particular content-type. You can't more content type. Follow only below two-step to resolve multiple content type issue.

  1. Click on the checkbox of Use Lambda Proxy integration

API gateway --> API --> method --> integration request

enter image description here

enter image description here

  1. Create your response as

        let response = {
    
          statusCode: 200,
          headers: {
            'Content-type': 'application/pdf',//you can change any content type
            'content-disposition': 'attachment; filename=test.pdf' // key of success
          },
          body: buffer.toString('base64'),
          isBase64Encoded: true
        };
        return response;
    

Note* - It is not secure

Upvotes: 0

teocomi
teocomi

Reputation: 894

After spending several hours on this I found out that if you set Content handling to Convert to binary (CONVERT_TO_BINARY) the entire response has to be base64, I would otherwise get an error: Unable to base64 decode the body.

Therefore my response now looks like:

callback(null, buffer.toString('base64'));

The Integration response:

enter image description here

The Method response:

enter image description here

And Binary Media Types:

enter image description here

Upvotes: 19

Noel Llevares
Noel Llevares

Reputation: 16037

If you have a gigantic PDF, then it will take a long time for Lambda to return it and in Lambda you are billed per 100ms.

I would save it to S3 first then let the Lambda return the S3 url to the client for downloading.

Upvotes: 7

Related Questions