I'm having an issue where AWS lambda is corrupting my image buffer when I try and send it to a discord webhook. It works locally with SLS Offline and I can see image in discord channel with no issues, but when I deploy it to AWS I get
instead of the image itself. Looking around at similar people with this issue I've tried adding to my serverless.yml
plugins:
- serverless-apigw-binary
with apigwBinary
in custom path.
apigwBinary:
types: #list of mime-types
- 'image/png'
I also saw another post about adding AwsApiGateway
under provider in serverless.yml like so
provider:
AwsApiGateway:
binaryMediaTypes:
- 'image/png'
When I console.log('Sending Buffer', buffer);
the buffer to make sure it's actually there in cloudwatch I see
Sending Buffer <Buffer fd 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 04 fd 00 00 02.... 162236 more bytes>
So the buffer is definitely making it to the lambda, but it gets corrupted when it makes it to discord. But again it does work locally with no issues. So Lambda is corrupting it somehow. I even tried adding a 3 second delay to see if that might fix it as if it was a race condition or something even though I can see buffer in console log, but nope.
webhook.send({
files: [{
attachment: buffer,
name: newFileName
}]
})
.then((res) => {
console.log('FINISHED', res);
});
}
catch (e) {
console.log('ERROR', e);
}
UPDATE:
I also tried uploading the image to S3 to see if the image would be corrupted there and again the file is fine when uploading locally, but not when it's uploaded to S3 from Lambda. Same issue. Image is corrupted. I noticed also that the file size when uploading from SLS offline is a little bigger by like 50 bytes than the lambda version so there is some weird compression going on from cloudfront that could be causing the image corruption.
End to end code of sending the image from Angular to Lambda to Discord
component
const table = document.getElementById('table');
const canvas = await html2canvas(table);
console.log('canvas', canvas);
const image: any = await getCanvasBlob(canvas);
this.angularService
.postImageToDiscord(image)
.pipe(takeUntil(this.destroy))
.subscribe((res) => {
this.isLoading = false;
});
Angular Service
postImageToDiscord(imageData: File) {
let formData = new FormData();
formData.append('file', imageData, 'file.png');
const body = { file: imageData };
const api = environment.baseUrl + '/post-image';
const req = new HttpRequest('POST', api, formData);
return this.http.request(req);
}
Serverless function definition
functions:
sendImageToDiscord:
handler: src/handler.sendImageToDiscord
events:
- http:
path: /post-image
method: post
cors: true
handler.ts
import { parser } from './parser';
export const sendImageToDiscord = async (
event: any,
context: Context
): Promise<ProxyResult> => {
const parsedEvent: any = await parser(event);
try {
await postImageTableToDiscord(
parsedEvent.body.file,
event.queryStringParameters.assetType
);
} catch (e) {
console.log('ERROR HERE', e);
}
const response = {
statusCode: 200,
body: JSON.stringify('file sent successfully')
};
return response;
};
imported parser file from handler
const Busboy = require('busboy');
const getContentType = (event: any) => {
const contentType = event.headers['content-type'];
if (!contentType) {
return event.headers['Content-Type'];
}
return contentType;
};
export const parser = (event: any) =>
new Promise((resolve, reject) => {
const busboy = new Busboy({
headers: {
'content-type': getContentType(event)
}
});
let result: any = {
file: '',
filename: '',
contentType: ''
};
busboy.on('file', (fieldname, file, filename, encoding, mimetype) => {
file.on('data', (data) => {
result.file = data;
});
file.on('end', () => {
result.filename = filename;
result.contentType = mimetype;
});
});
busboy.on('field', (fieldname, value) => {
result[fieldname] = value;
});
busboy.on('error', (error: any) => reject(error));
busboy.on('finish', () => {
event.body = result;
resolve(event);
});
busboy.write(event.body, event.isBase64Encoded ? 'base64' : 'binary');
busboy.end();
});
I appreciate any help with this.
The issue was using busboy. There might be a way to get it working, but it was corrupting the image when parsing the blob to buffer. The way I got it working with a lambda function and serverless framework when deployed was with aws-multipart-parser. It was super straightforward and painless using this library with aws lambda. No corrupted image issues anymore.