1

My API end point returns an excel file streamed from S3. It works on the local but when testing on API gateway the file is corrupted. Here is my API code:

const downloadContentFromS3 = async function(bucket, file) {
  return new Promise((resolve, reject) => {
    streamFileFromS3(bucket, file, (error, s3buffer) => {
      if (error) return reject(error);
      return resolve(s3buffer);
    });
  });
};

const streamFileFromS3 = async function(bucket, fileName, callback) {
  const params = {
    Bucket: bucket,
    Key: fileName,
  };

  const buffers = [];
  const stream = s3.getObject(params).createReadStream();
  stream.on('data', data => buffers.push(data));
  stream.on('end', () => callback(null, Buffer.concat(buffers)));
  stream.on('error', error => callback(error));
};

downloadExcelFile: async (req, res) => {
  try {
    const fileName = 'myFilename';
    const workbook = await downloadContentFromS3(
      'bucket-name'        
      fileName
    );
    const workbook = xlsx.read(buffer);

    res.setHeader('Content-disposition', `attachment; filename=${fileName}`);
    res.setHeader(
      'Content-type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
    );
    const wbout = xlsx.write(workbook, { bookType: 'xlsx', type: 'buffer' });
    res.status(200).send(Buffer.from(wbout));
  } catch (error) {
    throw new OriolaError(error.message);
  }
},

What I have tried so far: Setup the binary media types as shown in the picture:enter image description here

In addition tried to set the RESPONSE HEADER to Content-Type and Content-Disposition but to no avail. The problem seems to persist. Any ideas and help is appreciated.

EDIT: I have also tried to set binary type */* in the settings but this does not help as well.

Hassan Abbas
  • 1,166
  • 20
  • 47

2 Answers2

5

API Gateway and Lambda send files between themselves as base64. This is regardless of whether you set a binary media type on API Gateway, as API Gateway does the conversion between base64 and binary.

S3 getObject gets a binary file, but that is a moot point for your problem, as you are still creating a binary file with xlsx.

What you are currently doing is sending binary data untransformed as base64 data.

All you need to do is return the file as a base64 buffer instead of a binary one.

So

res.status(200).send(Buffer.from(wbout));

becomes

res.status(200).send(Buffer.from(wbout).toString('base64'));
K Mo
  • 2,125
  • 8
  • 16
0

https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings.html

So either

  • setup passthrough behaviour and send binary response body from your function as you are already doing,
  • or set the contentHandling property of the IntegrationResponse resource to CONVERT_TO_BINARY in addition to binaryMediaTypes setting already done and then send base64 response body.
ckedar
  • 1,859
  • 4
  • 7