I have a Node.js + Express application running on EC2 instance. Part of that application is to send mass email to all its users (by the admin) with the ability to attach files (max 5 files allowed).
Recently we tested it by attaching three pdf files roughly the size of 2.5 MB each. When the send button is pressed the application keeps spinning before culminating to 504 Gateway Timeout error. I feel that maybe the sendgrid code is unable to process the attachments and the node server crashes taking down the EC2 with it. When this happens the only way for me is to stop the ec2 instance and then start it again. Rebooting does not help.
Here is the code
router.js
var fs = require('fs');
var multer = require('multer');
const uploadsDir = './uploads';
// SET STORAGE
var storage = multer.diskStorage({
destination: function (req, file, cb) {
if (!fs.existsSync(uploadsDir)){
fs.mkdirSync(uploadsDir);
}
cb(null, uploadsDir);
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
var upload = multer({ storage: storage });
router.post('/send', upload.array('files', 5), async (req, res) => {
let subject = req.body.subject;
let message = req.body.message;
let result = message.replace(/(\r\n|\r|\n)/g, '<br>');
let bccReceiverList = [];
let whereCondition = {
isActive: true
};
let attachments = [];
if(req.files && req.files.length > 0) {
req.files.forEach(file => {
attachments.push({
filename: file.originalname,
type: file.mimetype,
uploadPath: req.app.get('uploads')
});
});
}
let receiverUsers = await User.findAll({});
//find to whom we are sending the email to
for (let index = 0; index < receiverUsers.length; index++) {
const user = receiverUsers[index];
emailHandler.sendEmail(
{
receiver: user.email,
bccReceiver: bccReceiverList,
templateId: process.env.EMAIL_BLAST_TEMPLATE,
attachments: attachments.length > 0 ? attachments : []
},
{
subject: subject,
message: result
},
data => {}
);
}
if(req.files && req.files.length > 0) {
req.files.forEach(file => {
fs.unlink(req.app.get('uploads') + '/' + file.originalname, function (err) {
if (err) {
console.error(err);
}
console.log('File has been Deleted');
res.send('file was deleted');
});
});
}
res.redirect('back');
});
then in the actual email handler function
var sg = require('@sendgrid/mail');
var fs = require('fs');
sg.setApiKey(process.env.SENDGRID_API_KEY);
exports.sendEmail = async function(email, payload, callback) {
let msg = {
to: email.receiver,
from: {email: 'admin@myapp.com', name: 'My App'},
subject: email.subject,
templateId: email.templateId,
dynamic_template_data: payload
};
//Buffer.from(fileContent).toString('base64')
if(email.attachments != null && email.attachments.length > 0) {
try {
let attachmentObjects = [];
for (let index = 0; index < email.attachments.length; index++) {
const attachment = email.attachments[index];
const fileContent = fs.readFileSync(attachment.uploadPath + '/' + attachment.filename);
attachmentObjects.push({
content: Buffer.from(fileContent).toString('base64'),
filename: attachment.filename,
type: attachment.mimetype,
disposition: "attachment"
});
}
msg.attachments = attachmentObjects;
} catch (error) {
console.log(error);
callback({status: 500, message: 'Error while attaching files to email: ' + error.message});
}
}
if(email.hasOwnProperty('ccReceiver')) {
if(email.ccReceiver != null) {
msg.cc = email.ccReceiver;
}
}
if(email.hasOwnProperty('bccReceiver')) {
if(email.bccReceiver.length > 0) {
msg.bcc = email.bccReceiver;
}
}
sg.send(msg).then(() => {
console.log('---- email sent successfully');
// delete the attachment files from the uploads folder
if(email.attachments != null && email.attachments.length > 0) {
for (let index = 0; index < email.attachments.length; index++) {
const attachment = email.attachments[index];
fs.unlink(attachment.uploadPath + '/' + attachment.filename, function (err) {
if (err) {
console.error(err);
}
console.log('File has been Deleted');
});
}
}
callback({status: 200, message: 'Email Sent Successfully'});
}).catch(error => {
//Log friendly error
console.error('error-------- : ' + error.toString());
//Extract error msg
const {
message,
code,
response
} = error;
//Extract response msg
const {
headers,
body
} = response;
callback({status: 500, message: 'Error while sending email: ' + error.message});
});
};
I even tried just attaching one pdf file (2.5 MB) to the email and it still failed. When I perform this same test with files with lesser size then it works smoothly. I am not really sure how to solve this problem.