34

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.

2
  • 1
    I'm curious about the Base64 encoding. Is that necessary? Mayb this is the problem? I'd return the data in binary format. Commented Jul 27, 2017 at 10:48
  • I tried some variations (also without bas64 encoding). The suggestion came from the following link : github.com/serverless/serverless/issues/2797 Commented Jul 27, 2017 at 10:51

6 Answers 6

41

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.

Sign up to request clarification or add additional context in comments.

13 Comments

Can you share 1) exactly what changes were done on the API Gateway 2) How are you handling base64 encoded response string on client ?
@Shuki just updated the answer, hope this also works for you
@sami_analyst this worked for me so thanks, weird that 'application/pdf' isn't working when I upload a pdf.
I set the binary media type to "application/pdf" and also ensured that the API Gateway - Method Response included an HTTP Status for "200" with a response body Content-type of "application/pdf" (Empty model). Plus a HTTP Status for "500" with a response body Content-type of "text/plain" (Empty model), for when an error occurs.
@HarisMehmood I separated the wkhtmltopdf into its own lambda function and have a dotnet core webapi invoking that lambda function to get the base64 encoded string. I then UTF8 Decode that string to a byte[] and return that as a file from the WebApi. My issue, was that the response from the webapi was an invalid pdf file. this was fixed after i added the following binary media type in the settings page on the apiGateway: */* as well as adding a 200 OK method response with content type "application/pdf" and "Empty" model. And re deployed the API Gateway. Let me know that fixes it for you
|
19

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

2 Comments

In case you go the SAM way, add below lines Globals: Api: BinaryMediaTypes: - application~1pdf
You can add the binary media type in serverless framework, you can see here Binary Media Type responses
7

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.

4 Comments

doesn't the upload process to s3 takes at least as much time ? thank you for this proposition, but i have to download the file directly
There is a 6MB response body size limit to watch out for as well
For protected files, how does the Lambda know what user uploaded the file? After uploading from my lambda and trying to access it from my app, I always get a 404 no key error.
@conor909 Try creating a new question for that.
2

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.

1 Comment

I had this issue. I had more than one content type listed. Your explanation helped me
0

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

Comments

0

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.