I'm trying to create a lambda function to read a zip file from s3 and to serve it. But after downloading this file in the browser I can't unzip it, getting the error "Unable to extract, it is in an unsupported format". What can be a problem?
const file = await s3.getObject({
Bucket: 'mybucket',
Key: `file.zip`
}).promise();
return {
statusCode: 200,
isBase64Encoded: true,
body: Buffer.from(file.Body).toString('base64'),
headers: {
'Content-Type': 'application/zip',
'Content-Disposition': `attachment; filename="file.zip"`,
},
}
Your file.Body should already be a Buffer, so Buffer.from(file.Body) should be unnecessary but unharmful.
I think your problem is that you're doing toString('base64') there. The documentation says:
If body is a binary blob, you can encode it as a Base64-encoded string by setting isBase64Encoded to true and configuring / as a Binary Media Type.
This makes me believe that it actually means that AWS will automatically convert your (non-base64) body into base64 in the response body. If that's the case, due to you doing .toString('base64'), your body is being base64'd twice. You could un-base64 your resulting file.zip and see what it gives.
The solution for me was to set 'Content-Encoding': 'base64' response header.
you can follow this code below
"use strict";
const AWS = require("aws-sdk");
const awsOptions = {
region: "us-east-1",
httpOptions: {
timeout: 300000 // Matching Lambda function timeout
}
};
const s3 = new AWS.S3(awsOptions);
const archiver = require("archiver");
const stream = require("stream");
const request = require("request");
const streamTo = (bucket, key) => {
var passthrough = new stream.PassThrough();
s3.upload(
{
Bucket: bucket,
Key: key,
Body: passthrough,
ContentType: "application/zip",
ServerSideEncryption: "AES256"
},
(err, data) => {
if (err) throw err;
}
);
return passthrough;
};
// Kudos to this person on GitHub for this getStream solution
// https://github.com/aws/aws-sdk-js/issues/2087#issuecomment-474722151
const getStream = (bucket, key) => {
let streamCreated = false;
const passThroughStream = new stream.PassThrough();
passThroughStream.on("newListener", event => {
if (!streamCreated && event == "data") {
const s3Stream = s3
.getObject({ Bucket: bucket, Key: key })
.createReadStream();
s3Stream
.on("error", err => passThroughStream.emit("error", err))
.pipe(passThroughStream);
streamCreated = true;
}
});
return passThroughStream;
};
exports.handler = async (event, context, callback) => {
var bucket = event["bucket"];
var destinationKey = event["destination_key"];
var files = event["files"];
await new Promise(async (resolve, reject) => {
var zipStream = streamTo(bucket, destinationKey);
zipStream.on("close", resolve);
zipStream.on("end", resolve);
zipStream.on("error", reject);
var archive = archiver("zip");
archive.on("error", err => {
throw new Error(err);
});
archive.pipe(zipStream);
for (const file of files) {
if (file["type"] == "file") {
archive.append(getStream(bucket, file["uri"]), {
name: file["filename"]
});
} else if (file["type"] == "url") {
archive.append(request(file["uri"]), { name: file["filename"] });
}
}
archive.finalize();
}).catch(err => {
throw new Error(err);
});
callback(null, {
statusCode: 200,
body: { final_destination: destinationKey }
});
};