Nodemailer body with qrcode image - node.js

I'm creating the server with express and want to send mail with QRcode inside the body
var express = require('express');
var app = express();
var nodeMailer = require('nodemailer');
var sql = require("mssql");
var bodyParser = require('body-parser');
var QRCode = require('qrcode')
app.use( bodyParser.json() ); // to support JSON-encoded bodies
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
extended: true
}));
app.post('/send-email', function (req, res) {
QRCode.toDataURL('data invoice untuk di kirim melalui email', function (err, url) {
let data = url.replace(/.*,/,'')
var img = new Buffer(data,'base64')
})
let transporter = nodeMailer.createTransport({
host: 'test',
port: 587,
secure: false,
auth: {
user: 'tes',
pass: 'password'
}
});
let mailOptions = {
from: 'test', // sender address
to: 'test', // list of receivers
subject: 'Test Email Node JS', // Subject line
text: 'Halo ini dari node js', // plain text body
html: 'Halo ini barcodenya </br>' + img // html body
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
//console.log('Message %s sent: %s', info.messageId, info.response);
res.render('index');
});
});
I get an error that img is not defined.
Variable img is what the qr code, and for the string it will get the string from SQL query.
Any source for this?

QRCode.toDataURL is a asynchronous function but your trying to use as synchronously. Use Promises and Async/Await in place of callback function.
Your sending a image as buffer but its need to be in base64 with image tag
Here are the code snippet changes,
...
app.post('/send-email', async function (req, res) {
let img = await QRCode.toDataURL('data invoice untuk di kirim melalui email');
let transporter = nodeMailer.createTransport({
host: 'test',
port: 587,
secure: false,
auth: {
user: 'tes',
pass: 'password'
}
});
let mailOptions = {
from: 'test', // sender address
to: 'test', // list of receivers
subject: 'Test Email Node JS', // Subject line
text: 'Halo ini dari node js', // plain text body
html: 'Halo ini barcodenya </br> <img src="' + img + '">' // html body
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
//console.log('Message %s sent: %s', info.messageId, info.response);
res.render('index');
});
});
...

in the mailOptions you need to add the attachDataUrls on true
let mailOptions = {
from: 'test', // sender address
to: 'test', // list of receivers
subject: 'Test Email Node JS', // Subject line
text: 'Halo ini dari node js', // plain text body
attachDataUrls: true,//to accept base64 content in messsage
html: 'Halo ini barcodenya </br> <img src="' + img + '">' // html body
};

Related

nodemailer not sent email in cloud function

I have this code in a cloud function where I want to use nodemailer to send some notification emails.
const transporter = nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
user: 'mygmailaddress#gmail.com',
pass: 'apppassword'
}
})
/* Send email for custom programs to unsigned patients */
exports.sendEmailToNewPatient = functions.https.onRequest( (req, res) => {
cors( req, res, () => {
const mailOptions = {
to: req.body.userEmail,
from: 'mygmailaddress#gmail.com',
subject: `${req.body.doctorName} test!`,
text: 'Test message',
html: '<h1>Test message/h1>'
}
transporter.sendMail(mailOptions, (error, info) => {
if( error ) {
res.send(error.message)
}
const sendMailResponse = {
accepted: info.accepted,
rejected: info.rejected,
pending: info.pending,
envelope: info.envelope,
messageId: info.messageId,
response: info.response,
}
res.send(sendMailResponse)
})
})
})
I'm calling the function using a POST request made with axios, when the request is send I will get a status code of 200 and in the data object of axios response I will have this informations
data:
code: "EENVELOPE"
command: "API"
When I check my test email address to verify if the email is sent, I will not have any message as expected.
Any suggestion about?
I can see a small mistake in your code that the heading tag is not closed in the html message.
For more reference on how to send the mail using node mailer you can follow the below code.
As mentioned in the link:
const functions = require("firebase-functions");
const nodemailer = require('nodemailer');
const smtpTransport = require('nodemailer-smtp-transport');
const cors = require("cors")({
origin: true
});
exports.emailMessage = functions.https.onRequest((req, res) => {
const { name, email, phone, message } = req.body;
return cors(req, res, () => {
var text = `<div>
<h4>Information</h4>
<ul>
<li>
Name - ${name || ""}
</li>
<li>
Email - ${email || ""}
</li>
<li>
Phone - ${phone || ""}
</li>
</ul>
<h4>Message</h4>
<p>${message || ""}</p>
</div>`;
var sesAccessKey = 'YOURGMAIL#gmail.com';
var sesSecretKey = 'password';
var transporter = nodemailer.createTransport(smtpTransport({
service: 'gmail',
auth: {
user: sesAccessKey,
pass: sesSecretKey
}
}));
const mailOptions = {
to: "myemail#myemail.com",
from: "no-reply#myemail.com",
subject: `${name} sent you a new message`,
text: text,
html: text
};
transporter.sendMail(mailOptions, function(error, info){
if(error){
console.log(error.message);
}
res.status(200).send({
message: "success"
})
});
}).catch(() => {
res.status(500).send("error");
});
})
;
For more information you can check the blog , thread and documentation where brief explanations including code is provided.
If all above has been followed well then you can check this thread for further details:
First of all, you have to enable the settings to allow less secure apps for the gmail account that you are using. Here is the link.
Secondly, Allow access for "Display Unlock captcha option" (Allow access to your Google account). Here is the link.
As mentioned by Frank van Puffelen ,here you can process POST data in Node.js using console.log(req.body.userEmail) for reference you can check link.

express-fileupload requires me to upload a file which is optional on the form

I have a challenge with express-fileupload when a user doesn't upload a file that is meant to be optional.
Someone should please help me out.
This is my code:
const file = req.files.document;
const file2 = req.files.document2;
const uploader = req.body.fullname;
const filename = `CV_${uploader}_${file.name}`;
const filename2 = `Cover_${uploader}_${file2.name}`;
let savedFile = filename.replace(/\s+/g, "");
let savedFile2 = filename2.replace(/\s+/g, "");
const path = "uploads/" + savedFile;
const path2 = "uploads/" + savedFile2;
file.mv(path, (err) => {
if (err) {
console.log(err);
}
});
file2.mv(path2, (err) => {
if (err) {
console.log(err);
}
});
The second file is optional for the user to upload. When the user doesn't upload it, it shows an error.
Please, how can I make it optional from here.
It shows an error like this:
Type Error: Cannot read property 'name' of undefined
Thank you so much.
So, I was able to find my way around the whole thing.
I did it like this...
app.post("/form", (req, res) => {
const file = req.files.document;
const file2 = req.files.document2;
const uploader = req.body.fullname;
const filename = `CV_${uploader}_${file.name}`;
let savedFile = filename.replace(/\s+/g, "");
const path = "uploads/" + savedFile;
file.mv(path, (err) => {
if (err) {
console.log(err);
}
});
// function to save file2 to server if it exists and send the filename to be used outside the function
const filename2 = file2 ? `Cover_${uploader}_${file2.name}` : null;
let savedFile2 = filename2 ? filename2.replace(/\s+/g, "") : null;
const path2 = filename2 ? "uploads/" + savedFile2 : null;
if (file2 && file2.name) {
const filename2 = `Cover_${uploader}_${file2.name}`;
let savedFile2 = filename2.replace(/\s+/g, "");
const path2 = "uploads/" + savedFile2;
file2.mv(path2, (err) => {
if (err) {
console.log(err);
}
});
}
// Saving to the database...
const date = new Date();
const dateNow = moment(date).format("llll");
const job = new Jobs({
position: req.body.positions,
language: req.body.lang,
fullName: req.body.fullname,
gender: req.body.gender,
education: req.body.education,
email: req.body.email,
address: req.body.address,
phone: req.body.phone,
fileCV: savedFile,
fileCover: savedFile2,
date: dateNow,
});
job.save((err) => {
if (!err) {
res.render("success");
}
});
// Sending to mail server
const output = `
<p> You have a new applicant! </p>
<h2> Contact Details </h2>
<ul>
<li>position: ${req.body.positions}</li>
<li>language: ${req.body.lang} </li>
<li>fullName: ${req.body.fullname}</li>
<li>gender: ${req.body.gender}</li>
<li>email: ${req.body.email}</li>
<li>address: ${req.body.address}</li>
<li>phone: ${req.body.phone}</li>
<li>education: ${req.body.education}</li>
</ul>
`;
const transporter = nodemailer.createTransport({
service: "localhost",
port: 1025,
secure: false, // true for 465, false for other ports
auth: {
user: "project.1", // generated ethereal user
pass: "secret.1", // generated ethereal password
},
tls: {
rejectUnauthorized: false,
},
});
let senderName = req.body.fullname;
let senderEmail = req.body.email;
//send mail with unicode symbols
let mailOptions = {
from: `"${senderName}" <${senderEmail}>`, // sender address
to: "mikejuwon737#gmail.com, sjobopisa#gmail.com", // list of receivers
subject: "Job Application ✔", // Subject line
text: "Check out my details here...", // plain text body
html: output, // html body
attachments: [
{ filename: `${savedFile}`, path: `${path}` },
{ filename: `${savedFile2}`, path: `${path2}` },
], // list of attachments
};
// sending mail with defined transport object
transporter.sendMail(mailOptions, (err, info) => {
if (err) {
console.log(err);
} else {
console.log("Message sent: %s", info.messageId);
// console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
}
});
});

How to send Email with attachment in angular using nodemialer?

My website careers page, User need to send their profile and then profile(doc or pdf) need to send as an attachment to the email on clicking submit button. Technologies I am using Angular6, NodeJs, Express, and Nodemailer to send the email.
Here is my peace of HTML code(careers.html),
<form (ngSubmit)="sendData()" >
<textarea [(ngModel)]="user.message"></textarea>
<input type="file" (change)="userProfile($event)" >
<small>Upload .docx or pdf file</small>
<button >Submit</button>
</form>
a bit of ts code (careers.ts),
user = {
message: '',
fileContent :''
};
userProfile(event: any) {
if (event.target.files && event.target.files[0]) {
var reader = new FileReader();
reader.onload = (event: any) => {
this.user.fileContent = event.target.result;
}
reader.readAsDataURL(event.target.files[0]);
}
sendData() {
this.careersService.sendWithAttachment(this.user);
}
here is service class(service.ts),
sendWithAttachment(userData) {
this.http.post("http://localhost:3000/uploadfile", userData
)
.subscribe(
data => {
console.log("Sent Request is successful ", data);
},
error => {
console.log("Error", error);
}
);
}
finally js file (app.js)
var mailOptions = {
from: '"User" <mail#gmail.com>', // sender address
to: "mail#mail.com", // list of receivers
subject: "Mail from Careers",
text: text,
html: html, // html body
attachments: [ {
filename: 'profile.pdf',
content: req.body.fileContent,
contentType: 'application/pdf'
}]
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('Message sent: ' + info.response);
});
Now I am able to receive email with attachement but that attachement is empty or corrupted. Any help is much appreciated.
You need to upload the file on the server, Maybe you can do it though multer and then you can pass the server file path to the Nodemailer Function.
Here are some code snippet for your reference,
var express = require('express')
var multer = require('multer')
var upload = multer({ dest: 'uploads/' })
var app = express()
app.post('/uploadfile', upload.single('profile'), function (req, res) {
var mailOptions = {
....
attachments: [{
filename: req.file.filename,
path: req.file.path
}]
};
...
})
Note : Attachment can be done in various methods in Nodemailer, Refer the documentation

Need simultaneous working of Axios post for data and file from React

I can either have my axios post send some data ( example below ) or FormData. How would I set the call up so that I can send it all at once. The problem is that if I send both simultaneously, it doesn't send anything at all. My current call is :
async handleSubmit(e) {
e.preventDefault();
const { name, email, message } = this.state;
const formData = new FormData();
formData.append('file',this.state.file)
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
const form = await axios.post("/api/formPDF", {
name, ******
email, *****
message ****
}).post("/api/formPDF", formData, config);
}
The section I have indicated with * is where I believe my problem to be. The way I have it send now, I will have access to name/email/message on req.body. If I remove the object of the three, and replace it with formData it will correctly email the file but everything is clearly undefined. If I edit it as so :
const form = await axios.post("/api/formPDF", {
name,
email,
message,
formData
It makes it so both my req.body and the way I parse my file is an empty object. My relevant server code is :
app.post("/api/formPDF", (req, res) => {
var fileLoc, fileExt, fileName, fileEmail, fileMessage;
var form = new formidable.IncomingForm();
form.parse(req, function(err, fields, files) {
console.log("Files: ", files);
fileLoc = files.file.path;
fileExt = files.file.name.split('.').pop();
});
nodemailer.createTestAccount((err, account) => {
const htmlEmail = `
<h3>Contact Details</h3>
<ul>
<li>Name: ${req.body.name}</li>
<li>Email: ${req.body.email}</li>
</ul>
<h3>Message</h3>
<p>${req.body.message}</p>
`
let transporter = nodemailer.createTransport({
name: *removed*,
host: *removed*,
port: 465,
secure: true,
auth: {
user: *removed*,
pass: *removed*
}
})
let mailOptions = {
from: *removed*,
to: *removed*,
replyTo: req.body.email,
subject: "New Message",
text: req.body.message,
html: htmlEmail,
attachments: [
{
filename: `${req.body.name}Resume.${fileExt}`,
path: fileLoc
}
]
};
fileLoc = "";
transporter.sendMail(mailOptions, (err, info) => {
if (err) {
return console.log(err)
}
})
})
});
Am I missing something that is causing the objects to be empty? I know its not possible to read the formData client side, but I should be able to see it on my server. Thanks in advance.

How to attach file to an email with nodemailer

I have code that send email with nodemailer in nodejs but I want to attach file to an email but I can't find way to do that I search on net but I could't find something useful.Is there any way that I can attach files to with that or any resource that can help me to attach file with nodemailer?
var nodemailer = require('nodemailer');
var events = require('events');
var check =1;
var events = new events.EventEmitter();
var smtpTransport = nodemailer.createTransport("SMTP",{
service: "gmail",
auth: {
user: "example#gmail.com",
pass: "pass"
}
});
function inputmail(){
///////Email
const from = 'example<example#gmail.com>';
const to = 'example#yahoo.com';
const subject = 'example';
const text = 'example email';
const html = '<b>example email</b>';
var mailOption = {
from: from,
to: to,
subject: subject,
text: text,
html: html
}
return mailOption;
}
function send(){
smtpTransport.sendMail(inputmail(),function(err,success){
if(err){
events.emit('error', err);
}
if(success){
events.emit('success', success);
}
});
}
///////////////////////////////////
send();
events.on("error", function(err){
console.log("Mail not send");
if(check<10)
send();
check++;
});
events.on("success", function(success){
console.log("Mail send");
});
Include in the var mailOption the key attachments, as follow:
var mailOptions = {
...
attachments: [
{ // utf-8 string as an attachment
filename: 'text1.txt',
content: 'hello world!'
},
{ // binary buffer as an attachment
filename: 'text2.txt',
content: new Buffer('hello world!','utf-8')
},
{ // file on disk as an attachment
filename: 'text3.txt',
path: '/path/to/file.txt' // stream this file
},
{ // filename and content type is derived from path
path: '/path/to/file.txt'
},
{ // stream as an attachment
filename: 'text4.txt',
content: fs.createReadStream('file.txt')
},
{ // define custom content type for the attachment
filename: 'text.bin',
content: 'hello world!',
contentType: 'text/plain'
},
{ // use URL as an attachment
filename: 'license.txt',
path: 'https://raw.github.com/andris9/Nodemailer/master/LICENSE'
},
{ // encoded string as an attachment
filename: 'text1.txt',
content: 'aGVsbG8gd29ybGQh',
encoding: 'base64'
},
{ // data uri as an attachment
path: 'data:text/plain;base64,aGVsbG8gd29ybGQ='
}
]}
Choose the option that adjust to your needs.
Link:Nodemailer Repository GitHub
Good Luck!!
Your code is almost right, just need to add, "attachments" property for attaching the files in your mail,
YOUR mailOption:
var mailOption = {
from: from,
to: to,
subject: subject,
text: text,
html: html
}
Just add attachments like
var mailOption = {
from: from,
to: to,
subject: subject,
text: text,
html: html,
attachments: [{
filename: change with filename,
path: change with file path
}]
}
attachments also provide some other way to attach file for more information check nodemailer community's documentation HERE
If you are passing options object in mail composer constructor and attachment is on http server then it should look like:
const options = {
attachments = [
{ // use URL as an attachment
filename: 'xxx.jpg',
path: 'http:something.com/xxx.jpg'
}
]
}
var express = require('express');
var router = express(),
multer = require('multer'),
upload = multer(),
fs = require('fs'),
path = require('path');
nodemailer = require('nodemailer'),
directory = path.dirname("");
var parent = path.resolve(directory, '..');
// your path to store the files
var uploaddir = parent + (path.sep) + 'emailprj' + (path.sep) + 'public' + (path.sep) + 'images' + (path.sep);
/* GET home page. */
router.get('/', function(req, res) {
res.render('index.ejs', {
title: 'Express'
});
});
router.post('/sendemail', upload.any(), function(req, res) {
var file = req.files;
console.log(file[0].originalname)
fs.writeFile(uploaddir + file[0].originalname, file[0].buffer, function(err) {
//console.log("filewrited")
//console.log(err)
})
var filepath = path.join(uploaddir, file[0].originalname);
console.log(filepath)
//return false;
nodemailer.mail({
from: "yourgmail.com",
to: req.body.emailId, // list of receivers
subject: req.body.subject + " ✔", // Subject line
html: "<b>" + req.body.description + "</b>", // html body
attachments: [{
filename: file[0].originalname,
streamSource: fs.createReadStream(filepath)
}]
});
res.send("Email has been sent successfully");
})
module.exports = router;
attachments: [
{
filename: "inovices_1.pdf", // the file name
path: "https://*************************/invoice/10_9_RMKUns.pdf",// link your file
contentType: "application/pdf", //type of file
},
{
filename: "inovices_2.pdf",
path: "https://**************************/invoice/10_9_RMKUns.pdf",
contentType: "application/pdf",
},
];
var nodemailer = require("nodemailer");
var all_transporter = nodemailer.createTransport({
host: process.env.MAIL_SERVICE,
port: 587,
auth: {
user: process.env.MAIL_USER,
pass: process.env.MAIL_PASS,
},
maxConnections: 3,
pool: true,
});
exports.send_email = function (email, subject, html, extra_cc = [], attachments = []) {
return new Promise(async (resolve, reject) => {
var mailOptions = {
from: process.env.MAIL_FROM_ADDRESS,
to: email,
subject: subject,
html: html,
cc: [],
};
mailOptions["cc"] = mailOptions["cc"].concat(extra_cc);
if (attachments.length > 0) mailOptions["attachments"] = attachments;
all_transporter.sendMail(mailOptions, function (error, info) {
// console.log(error);
// console.log(info);
if (error) {
resolve({ failed: true, err: error });
} else {
resolve({ failed: false, data: info.response });
}
});
});
};
The alternative solution is to host your images online using a CDN and link to the online image source in your HTML, eg. <img src="list_image_url_here">.
(I had problems with nodemailer's image embedding using nodemailer version 2.6.0, which is why I figured out this workaround.)
An added benefit of this solution is that you're sending no attachments to nodemailer, so the sending process is more streamlined.
var mailer = require('nodemailer');
mailer.SMTP = {
host: 'host.com',
port:587,
use_authentication: true,
user: 'you#example.com',
pass: 'xxxxxx'
};
Then read a file and send an email :
fs.readFile("./attachment.txt", function (err, data) {
mailer.send_mail({
sender: 'sender#sender.com',
to: 'dest#dest.com',
subject: 'Attachment!',
body: 'mail content...',
attachments: [{'filename': 'attachment.txt', 'content': data}]
}), function(err, success) {
if (err) {
// Handle error
}
}
});
Just look at here. Nodemailer > Message configuration > Attachments
The code snippet is below (pdfkit gets the stream):
// in async func
pdf.end();
const stream = pdf;
const attachments = [{ filename: 'fromFile.pdf', path: './output.pdf',
contentType: 'application/pdf' }, { filename: 'fromStream.pdf', content: stream, contentType: 'application/pdf' }];
await sendMail('"Sender" <sender#test.com>', 'reciver#test.com', 'Test Send Files', '<h1>Hello</h1>', attachments);
Stream uses content not streamSource This bothered me before, share with everyone :)
Reference = https://nodemailer.com/message/attachments/
var mailOption = {
from: from,
to: to,
subject: subject,
text: text,
html: html,
attachments: [
{
filename: filename,
path: filePath
},
]
}

Resources