Search code examples
aws-lambdagroovypostmanaws-api-gateway

Downloading CSV/XLSX file from API Gateway/Lambda POST request via Postman


I have this API endpoint, that is connected to this Lambda Function, written in Groovy, that consumes multipart/form-data and returns a CSV or XLSX file.

I have the Lambda Function up and running, and it returns the CSV/XLSX file as base 64 string.

However, before putting in any more work on the app, I wish to be able to see that file be downloaded from Postman request.

What does your Function return?

A response whose body is base-64 encoding of the File to be returned.

What does the Response Header look like ?

like this:

Date: Tue, 17 Oct 2023 03:49:15 GMT
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
Content-Length: 31680
Connection: keep-alive
x-amzn-RequestId: 7aeba6af-b8e7-4e01-93b2-c7c01bf803ac
Content-Disposition: attachment; filename=Returned Postcards 0ct 4 2023.XLSX
x-amz-apigw-id: M7WXME3EiYcFyrw=
X-Amzn-Trace-Id: Root=1-652e042d-069a3c126955e0a648434148;Sampled=0;lineage=bad4f2c2:0

Note that, we defined our response header, in the code, to be:

    Map<String, String> createResponseHeaders(String outputFileName) {
        return [
                ('Content-Type') : this.getContentTypeHeader(outputFileName),
                ('Content-Disposition') : "attachment; filename=${outputFileName}".toString(),
        ]
    }

    private String getContentTypeHeader(String outputFileName) {
        final String fileExtension = FileUtils.GetFileExtension(outputFileName).toLowerCase();
        switch (fileExtension) {
            case "xlsx":
                return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            case "csv":
                return "text/csv";
        }

        throw new IllegalArgumentException("Content type for extension '${fileExtension}' not yet implemented");
    }

On similar note, let's see that code for returning the response....

Sure... It ends up being something like this...

    return new APIGatewayProxyResponseEvent()
                .withStatusCode(200)
                .withHeaders(this.createResponseHeaders(outputFile.getName()))
                .withIsBase64Encoded(true)
                .withBody(Base64.getEncoder()
                        .encodeToString(outputFile.bytes))

What happens when you Send and Download??

When I send and download, I get a .txt file containing the raw base-64 output. I don't see the XLSX file I requested...

What do your API Gateway settings look like?

This:

enter image description here

How does your Postman request headers look like?

Just the Postman-generated defaults:

Content-Type: multipart/form-data; boundary=<calculated when request is sent>
...
User-Agent: PostmanRuntime/7.33.0
Accept: */*
Accept-Encoding: gzip, deflate, br
Connection: keep-alive

How do I get this such that the response causes the file to actually download?!


Solution

  • I was able to fix the issue!

    What I did

    On the API Gateway page, for my endpoint, I do the following:

    Adjust HTTP request headers

    • Click Method request tab
    • Click Edit button
    • Add the following names to HTTP request headers
      • Accept
      • Content-Type

    It should look like this in the console if you're trying this:

    enter image description here

    Adjust method response

    • Click Method response tab
    • Click Edit button
    • Add the following content types to the response body:
      • application/vnd.openxmlformats-officedocument.spreadsheetml.sheet (empty model)
      • text/csv (empty model)

    It should look like this on your console if you're trying this:

    enter image description here

    On the Postman side, I simply assign to Accept header the desired Content-Type (in this example, it is application/vnd.openxmlformats-officedocument.spreadsheetml.sheet).

    Adjust my source code a little bit

    I made typo in my createResponseHeaders() method. I fix it:

    Map<String, String> createResponseHeaders(String outputFileName) {
        return [
                ('Content-Type') : this.getContentTypeHeader(outputFileName),
                ('Content-Disposition') : "attachment; filename=\"${outputFileName}\"".toString(),
        ]
    }
    

    I then do that Send and Download... It worked!

    enter image description here enter image description here