email attachment in Angular 7 and Node.js - node.js

I have a contact form that send email with attachment using Angular 7 and node.js,
it works fine when you send email with attachment but it doesnt work without attachment
this is contact form
Typescript
onSubmitClick() {
const formData: FormData = new FormData();
this.fileToUpload && formData.append("file", this.fileToUpload, this.fileToUpload.name);
for (const key of Object.keys(this.form.value)) {
const value = this.form.value[key];
formData.append(key, value);
}
this.onSubmitClick; {
this.form.reset();
}
this.httpClient.post("http://localhost:3000/api/v1/contact/", formData)
.subscribe((response: any) => console.log(response));
}
handleFileInput(files: FileList) {
this.fileToUpload = files.item(0);
}
}
Node.js file
const mailOptions = {
from: 'xxxxxxx#xxx.com',
to: 'xxxxxxx#xxx.com',
subject: 'Segnalazione Portale',
//text: req.body.Nome,
html:'<b>Nome: </b>'+req.body.Nome+
'<br><b>Cognome: </b>'+req.body.Cognome
+'<br><b>Codice Fiscale: </b>'+req.body.CF
+'<br><b>Email: </b>'+req.body.Email
+'<br><b>Messagio: </b>'+req.body.message,
attachments: [
{
filename: file.name,
path: "tmp/" + file.name
}
]
};
transporter.sendMail(mailOptions, function (err, info) {
if (err) {
console.log(err);
return res.json({ err });
} else {
console.log(info);
return res.json({ info });
}
});
});
}
});
module.exports = router;
any idea why?
thanks in advance!

Related

Downloading excel file using ExcelJS and node js not being downloaded in any browser

I was trying to send .xlsx file using exceljs and nodejs to the client as a downloadable file. So far I'm able to create employees.xlsx file but it's not triggering save file dialog in the browser. Here's what I've tried in my backend API:
My Controller:
exports.exportEmployees = async (req, res) => {
try {
const employees = await Employee.find();
let workbook = new Excel.Workbook();
let worksheet = workbook.addWorksheet("Employees");
worksheet.columns = [
{ header: "Employee Name", key: "fullName", width: 40 },
{ header: "Department", key: "departmentName", width: 25 },
{ header: "Position", key: "positionName", width: 25 },
];
worksheet.addRows(employees);
res.setHeader(
"Content-Type",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
);
res.setHeader("Content-Disposition", "attachment; filename=employees.xlsx");
// This doesn't work either
// workbook.xlsx.write(res).then(function () {
// res.status(200).end();
// });
workbook.xlsx.writeFile("./employees.xlsx").then(
(response) => {
console.log("File is created"); // I'm able to see this in my console
console.log(path.join(__dirname, "../employees.xlsx"));
res.sendFile(path.join(__dirname, "../employees.xlsx"));
},
(err) => {
console.log("ERROR: ", err);
}
);
} catch (err) {
res.status(500).json({ errors: err });
}
};
And in my route.js
router.get("/employee-excel", auth, exportExcelController.exportEmployees);
module.exports = router;
So, the problem I'm facing now is whenever I call the api from my Angular app the browser response is a binary code.
Angular service to call the API
generateEmployeeExcel(query: any): Observable<any> {
return this.http.get(`${this.serverReportAPI}/employee-excel`, {
params: query,
headers: this.tokenHelperService.getAuthToken().headers,
});
}
My component
this.reportService.generateEmployeeExcel(query).subscribe(
(response) => {
console.log("Are we getting here?", response); // not able to get here
this.isLoading = false;
},
(err) => {
console.log("Is there any error?", err); // This is displayed in console
this.isLoading = false;
}
);
Any help would be appreciated.
The response in my browser
Change http code to :-
generateEmployeeExcel(query: any): Observable<any> {
return this.http.get(`${this.serverReportAPI}/employee-excel`, {
params: query,
headers: this.tokenHelperService.getAuthToken().headers,
responseType: 'blob'
});
}
For subscribe and download :-
this.reportService.generateEmployeeExcel(query).subscribe(
(res) => {
console.log("Are we getting here?", res); // not able to get here
this.isLoading = false;
let url = window.URL.createObjectURL(res.data);
let a = document.createElement('a');
document.body.appendChild(a);
a.setAttribute('style', 'display: none');
a.href = url;
a.download = "employee.xlsx";
a.click();
window.URL.revokeObjectURL(url);
a.remove();
},
(err) => {
console.log("Is there any error?", err); // This is displayed in console
this.isLoading = false;
}
);
If support for IE is required :-
this.reportService.generateEmployeeExcel(query).subscribe(
(res) => {
console.log("Are we getting here?", res); // not able to get here
if(window.navigator.msSaveBlob) {
window.nagigator.msSaveBlob(res, "employee.xlsx");
return;
}
this.isLoading = false;
let url = window.URL.createObjectURL(res.data);
let a = document.createElement('a');
document.body.appendChild(a);
a.setAttribute('style', 'display: none');
a.href = url;
a.download = "employee.xlsx";
a.click();
window.URL.revokeObjectURL(url);
a.remove();
},
(err) => {
console.log("Is there any error?", err); // This is displayed in console
this.isLoading = false;
}
);

How to add item with image with react-redux-saga with nodejs/multer

I am trying to save an item that has images in the form. I was able to save the items using postman, but getting issue when trying from react-redux-saga. I am using ant design for the form.
Front-end code:
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields((err, values) => {
if (!err) {
const { fileList } = this.state;
const formData = new FormData();
fileList.forEach(file => {
formData.append("files[]", file);
});
const postItemData = { ...values, images: formData };
this.props.createItem(postItemData);
}
});
};
saga:
When I try to console, I can get the item details with the image field as a formdata
{
let itemData = yield select(makeSelectPostItemData());
const token = yield select(makeSelectToken());
console.log(itemData, "itemData");
const response = yield call(request, `/item`, {
method: "POST",
headers: {
Authorization: `${token}`
},
body: JSON.stringify(itemData)
});
const successMessage = "Item saved successfully!";
yield put(postItemSuccess(response, successMessage));
}
nodejs:
const upload = multer({
storage: storage,
limits: { fileSize: 1000000 },
fileFilter: function(req, file, cb) {
checkFileType(file, cb);
}
}).array("images");
upload(req, res, err => {
if (err) {
console.log(err, "error");
res.send({ msg: err });
} else {
console.log(req, "req");
if (req.files === undefined) {
res.send({ msg: "Error: No file selected!" });
} else {
const { errors, isValid } = validatePostItem(req.body);
// check validation
if (!isValid) {
return res.status(400).json(errors);
}
const files = req.files;
}
}
});
On the last line const files = req.files. When I try from postman, I get file details, but when I try to request it from react, I have no req.files. How can I get the req.files from the react as I am getting from postman?
I have found the solution for this. In the handleSubmit, I have following code:
fileList.forEach(file => {
formData.append("files[]", file);
});
const postItemData = { ...values, images: formData };
this.props.createItem(postItemData);
Instead of spreading values and inserting the formData in images, what I did was put the all of them into formData.
fileList.forEach(file => {
formData.append("files[]", images);
});
formData.append("name", values.name);
formData.append("price", values.price);
formData.append("detail", values.detail);
this.props.createItem(formData);
With this, I could get req.files and req.body.name, req.body.price, req.body.detail in the node side. Quite different from the postman, but this worked for me.

How to send an email using AWS in Node project

My Angular app sends some data to a node server (app.js) via a POST request, the request body is then returned in the response.
I am now trying to send an email that contains this data sent in the request body.
Currently, I can read a HTML file to populate the email body, & then send the email but I need to replace that HTML file with the data sent in my req.body.
Here is what I have so far in app.js:
const express = require('express')
const app = express()
const bodyParser = require('body-parser')
app.post('/postData', bodyParser.json(), (req, res) => {
res.json(req.body)
readFile();
sendEmail();
})
app.listen(3000, () => console.log('Example app listening on port 3000!'))
var AWS = require('aws-sdk');
const fs = require('fs');
var params;
var htmlFileName = '/Users/myName/Desktop/test.html'
AWS.config.loadFromPath('config-aig.json');
const fromEmail = 'myName';
const toEmail = 'myName'
const subject = 'Test Email' + Date()
function sendEmail() {
// Create the promise and SES service object
var sendPromise = new AWS.SES({ apiVersion: '2010-12-01'}).sendEmail(params).promise();
sendPromise.then(
function (data) {
console.log('send email success');
}).catch(
function (err) {
console.error('error --> ', err, err.stack);
});
}
function readFile(callback) {
return new Promise(
function (resolve, reject) {
fs.readFile(htmlFileName, 'utf8',
function read(err, data) {
if (err) {
return reject(err)
}
else {
console.log('file read success');
return resolve(data);
}
})
}
)
}
readFile()
.then((res) => {
// Create sendEmail params
params = {
Destination: { /* required */
ToAddresses: [
toEmail,
]
},
Message: { /* required */
Body: { /* required */
Html: {
Charset: "UTF-8",
Data: res
}
},
Subject: {
Charset: 'UTF-8',
Data: subject
}
},
Source: fromEmail, /* required */
}
sendEmail();
})
.catch((err) => {
console.log('File Read Error : ', err)
}
)
Can someone please show me how I can replace my htmlFileName with the req.body?
I use ejs to template my email here is a code i frenquently use to send email !
If you have questions i would be glade to answer
const ejs = require('ejs');
const AWS = require('aws-sdk');
const mailcomposer = require('mailcomposer');
const config_aws = {
accessKeyId: '',
secretAccessKey: '',
region: 'eu-west-1',
expeditor: '',
limitExpeditor: 50
};
AWS.config.update(config_aws);
const ses = new AWS.SES();
async function sendAnEmail(
expeditor,
subject,
destinator,
body,
destinator_name = null,
bcc = null,
callback
) {
ejs.renderFile(
`${__dirname}/templates/template.ejs`,
{
destinator,
destinator_name,
subject,
body
},
(err, html) => {
if (err) return console.error(err);
const sesEmailProps = {
Source: config_aws.expeditor,
Destination: {
ToAddresses: [`${destinator}`]
},
Message: {
Body: {
Html: {
Charset: 'UTF-8',
Data: html
},
Text: {
Charset: 'UTF-8',
Data: html ? html.replace(/<(?:.|\n)*?>/gm, '') : ''
}
},
Subject: {
Charset: 'UTF-8',
Data: subject
}
}
};
if (bcc) {
sesEmailProps.Destination = {
ToAddresses: [`${destinator}`],
BccAddresses: bcc // ARRAY LIMIT OF 49
};
}
ses.sendEmail(sesEmailProps, (error, data) => {
if (error) console.error(error);
callback(error, data);
});
}
);
}

Using Jade mail template in Nodemailer

I have a contactform created with Nodemailer. Now I want a Jade tempate mail being send whenever the customer submits the contactform.
I already got it working and the mail template is already being send, but somehow the content of the Jade file is being presented in the 'subject' header of the mail. And everyting is presented with all the HTML tags. So, somewhere it goes wrong.
This is my Nodemailer code:
router.post('/contact/send', function(req, res) {
var transporter = nodeMailer.createTransport({
service : 'Gmail',
auth : {
user: process.env.GMAIL_USER,
pass: process.env.GMAIL_PASS
}
});
var mailOptions = {
from: req.body.name + ' <' + req.body.email + '>',
to: 'xxxxx#gmail.com',
subject:'Website verzoek',
text:'Er is een website verzoek binnengekomen van '+ req.body.name+' Email: '+req.body.email+'Soort website: '+req.body.website+'Message: '+req.body.message,
html:'<p>Websiteverzoek van: </p><ul><li>Naam: '+req.body.name+' </li><li>Email: '+req.body.email+' </li><li>Soort website: '+req.body.website+' </li><li>Message: '+req.body.message+' </li></ul>'
};
transporter.sendMail(mailOptions, function (err, info) {
if(err) {
console.log(err);
res.redirect('/#contact');
} else {
console.log('Message send');
res.redirect('/#contact');
}
});
var toAddress = req.body.email;
var sendMail = function(toAddress, subject, content, next) {
var mailTemplate = {
from: 'xxxxxx#gmail.com',
to: toAddress,
subject: subject,
html: content
};
transporter.sendMail(mailTemplate, next);
};
var template = process.cwd() + '/views/mails/mail.jade';
fs.readFile(template, 'utf8', function(err, file) {
if (err) {
console.log('Error');
} else {
var compiledTmpl = jade.compile(file, {filename: template});
var context = {title: 'Express'};
var html = compiledTmpl(context);
sendMail(toAddress, html, function(err, response) {
if(err) {
console.log('ERROR!');
} else {
console.log('Template send');
}
});
}
});
});
The problem is a typo mistake. Your sendMail function takes subject as second paramter.
var sendMail = function(toAddress, subject, content, next) {
var mailTemplate = {
from: 'xxxxxx#gmail.com',
to: toAddress,
subject: subject,
html: content
};
transporter.sendMail(mailTemplate, next);
};
Your are passing the compiled html as a second parameter to the function. So it takes the html as header.
sendMail(toAddress, html, function(err, response) {
if(err) {
console.log('ERROR!');
} else {
console.log('Template send');
}
});
Cheers.

Nodejs Immediately delete generated file

I'm trying to delete a pdf immediately after is has been generated in node.js. The generated pdf is sent as an email attachment, uploaded to dropbox and then deleted from the local file system. But as i try to delete it , it does not delete it and it does not send the email either. The pdf is created using html-pdf. Here is my code :
if (result) {
var filename = user.number+ ".pdf";
var path = './public/files/'+filename ;
var options = { filename: path, format: 'Legal', orientation: 'portrait', directory: './public/files/',type: "pdf" };
html = result;
pdf.create(html, options).toFile(function(err, res) {
if (err) return console.log(err);
console.log(res);
});
var dbx = new dropbox({ accessToken: mytoken });
fs.readFile( path,function (err, contents) {
if (err) {
console.log('Error: ', err);
}
dbx.filesUpload({ path: "/"+filename ,contents: contents })
.then(function (response) {
console.log("done")
console.log(response);
})
.catch(function (err) {
console.log(err);
});
});
var mailOptions = {
from: 'xyz', // sender address
to: user.email, // list of receivers
subject: 'Confirmation received', // Subject line
attachments : [{
filename: filename,
path : path
}]
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('Message %s sent: %s', info.messageId, info.response);
});
fs.unlinkSync(path); // even tried fs.unlink , does not delete file
// fs.unlinkSync(someother file); this one works
}
So when i do fs.unlink' orfs.unlinkSync`, if the file is already there it works but the files that is generated as in path doesn't get deleted.
NodeJs is asynchronous so you need to handle every blocks properly. your code shows that some times before completely creating the PDF itself the file upload to dropbox will start if the PDF creation is slow.
And deletion of the PDF file happens before the mail sending, so you get some err but you did not logged you error in fs.unlink(). Divide you code as blocks and use callback for better performance and flow.
Your code should be like this to work properly..
if (result) {
var filename = user.number+ ".pdf";
var path = './public/files/'+filename ;
var options = { filename: path, format: 'Legal', orientation: 'portrait', directory: './public/files/',type: "pdf" };
html = result;
//Generate the PDF first
pdf.create(html, options).toFile(function(err, res) {
if (err){
return console.log(err);
} else {
//If success then read the PDF file and then upload to dropbox
var dbx = new dropbox({ accessToken: mytoken });
fs.readFile( path,function (err, contents) {
if (err) {
console.log('Error: ', err);
} else {
dbx.filesUpload({path: "/"+filename ,contents: contents }).then(function (response) {
// Once the file upload is done then send mail
console.log("done")
sendMail('xyz', user.email, 'Confirmation received', filename, path, function(err, result){
// once mail is successful then delete the file finally
fs.unlinkSync(path); //if you need you can use callback with this for confirmation of deletion
});
}).catch(function(err) {
console.log(err);
});
}
});
}
});
function sendMail(sender, receiver, subject, filename, path, callback){
var mailOptions = {
from: sender, // sender address
to: receiver, // list of receivers
subject: subject, // Subject line
attachments : [{
filename: filename,
path : path
}]
};
transporter.sendMail(mailOptions, (err, info) => {
if (error) {
callback(err, null);
} else {
console.log('Message %s sent: %s', info.messageId, info.response);
callback(null, info)
}
});
}
}

Resources