在Nodemailer中向HTML模板传递变量

57

我想使用nodemailer发送带有HTML模板的电子邮件。在该模板中,我需要动态注入一些变量,但是我无法实现。我的代码:

var nodemailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');

smtpTransport = nodemailer.createTransport(smtpTransport({
    host: mailConfig.host,
    secure: mailConfig.secure,
    port: mailConfig.port,
    auth: {
        user: mailConfig.auth.user,
        pass: mailConfig.auth.pass
    }
}));
var mailOptions = {
    from: 'my@email.com',
    to : 'some@email.com',
    subject : 'test subject',
    html : { path: 'app/public/pages/emailWithPDF.html' }
};
smtpTransport.sendMail(mailOptions, function (error, response) {
    if (error) {
        console.log(error);
        callback(error);
    }
});

假设我想在emailWithPDF.html中添加如下内容:

Hello {{username}}!

我找到了一些例子,其中有这样的内容:

...
html: '<p>Hello {{username}}</p>'
...

但我想把它放在单独的HTML文件中。这可能吗?

12个回答

98

您可以使用Node中的fs模块读取HTML文件,然后使用handlebars在html字符串中替换要更改的元素。

var nodemailer = require('nodemailer');
var smtpTransport = require('nodemailer-smtp-transport');
var handlebars = require('handlebars');
var fs = require('fs');

var readHTMLFile = function(path, callback) {
    fs.readFile(path, {encoding: 'utf-8'}, function (err, html) {
        if (err) {
           callback(err);                 
        }
        else {
            callback(null, html);
        }
    });
};

smtpTransport = nodemailer.createTransport(smtpTransport({
    host: mailConfig.host,
    secure: mailConfig.secure,
    port: mailConfig.port,
    auth: {
        user: mailConfig.auth.user,
        pass: mailConfig.auth.pass
    }
}));

readHTMLFile(__dirname + 'app/public/pages/emailWithPDF.html', function(err, html) {
    if (err) {
       console.log('error reading file', err);
       return;
    }
    var template = handlebars.compile(html);
    var replacements = {
         username: "John Doe"
    };
    var htmlToSend = template(replacements);
    var mailOptions = {
        from: 'my@email.com',
        to : 'some@email.com',
        subject : 'test subject',
        html : htmlToSend
     };
    smtpTransport.sendMail(mailOptions, function (error, response) {
        if (error) {
            console.log(error);
        }
    });
});

在处理 .pug 文件时,我们可以通过传递对象给映射变量来实现相同的方式。是否有其他类似的方法? - Pardeep Jain
2
这个支持内联CSS样式吗? - Gaurav Sharma
2
是的,可以将CSS样式添加到您的HTML页面中。 - Ananth Pai
2
谢谢,@AnanthPai,您发布的答案真的很有帮助。起初,我对使用handlebars、express-handlebars等动态电子邮件模板非常困惑,现在已经清楚了。 - Bhagvat Lande
@AnanthPai,smtpTransport.sendMail函数中的回调是什么?它抛出“未定义回调”的错误。 - Jayna Tanawala
显示剩余2条评论

23

我在所有的项目中都使用它。更加干净、最新和易懂。回调地狱不存在。 sendMail.ts 这个HTML文件使用handlebar读取,将相关变量放入内容中并发送。

import * as nodemailer from 'nodemailer';
import * as handlebars from 'handlebars';
import * as fs from 'fs';
import * as path from 'path';

export async function sendEmail(email: string, subject: string, url: string) {
  const __dirname = path.resolve();
  const filePath = path.join(__dirname, '../emails/password-reset.html');
  const source = fs.readFileSync(filePath, 'utf-8').toString();
  const template = handlebars.compile(source);
  const replacements = {
    username: "Umut YEREBAKMAZ"
  };
  const htmlToSend = template(replacements);
  const transporter = nodemailer.createTransport({
    host: "smtp.mailtrap.io",
    port: 2525, // 587
    secure: false,
    auth: {
      user: "fg7f6g7g67",
      pass: "asds7ds7d6"
    }
  });
  const mailOptions = {
    from: '"noreply@yourdomain.com" <noreply@yourdomain.com>',
    to: email,
    subject: subject,
    text: url,
    html: htmlToSend
  };
  const info = await transporter.sendMail(mailOptions);
  console.log("Message sent: %s", info.messageId);
  console.log("Preview URL: %s", "https://mailtrap.io/inboxes/test/messages/");

}

2
你能用本地图片在HTML模板中这样使用吗?尝试使用了一个但是它无法加载。当然,使用URL是可以的。 - Johnny Navarro
如果您提供服务器上文件的绝对路径,您可以使用它。 例如:https://yourdomain.com/images/logo.png,您可以像传统HTML一样使用它。在官方网站的文档中,他们解释了另一种方法。 https://community.nodemailer.com/using-embedded-images/我仍然发现我的方法更容易和更灵活。我首先准备好我的HTML模板,然后将其包含在工作中。 - umutyerebakmaz
我也做了同样的事情。但是我的CSS没有与HTML链接起来。可能的原因是什么? - rickster
如果您要在电子邮件中使用CSS,请确保使用内联样式。 - umutyerebakmaz
1
2023,这个在我的测试中是有效的。不过,在我的情况下,我需要添加const __dirname = path.resolve();来解决__dirname未定义的问题。代码整洁。谢谢! - user6684898

11

使用replace方法替换字符串不是一个好主意,因为你将不得不恢复旧字符串或创建备份文件才能再次更改它们,而且它无法异步执行,会导致各种问题!

你可以更简单更清晰地完成这项任务:

只需转到您的邮件选项并添加带有变量的上下文即可:

var mailOptions = {
  from: 'nginx-iwnl@gmail.com',
  to: 'username@gmail.com',
  subject: 'Sending email',
  template: 'yourTemplate',
  context: {                  // <=
    username: username,
    whatever: variable
  }
};

接下来要做的是打开您的html文件并像这样调用变量:

{{用户名}}


尝试过但对我无效,还需要进行其他设置吗?@larachiwnl - RollerCosta

8
创建一个名为emailTemplates.js的文件,你可以将每个模板作为一个函数存储在其中。
const newsLetterEmail = (clientName) => `<p>Hi ${clientName}, here you have today news.</p>`
const welcomeEmail = (clientName, username) => `<p>Welcome ${clientName}, your username is ${username}.</p>`

export {newsLetterEmail, welcomeEmail}

然后在控制器中调用任何模板函数并将其存储在输出变量中。

controller.js

import {welcomeEmail} from './emailTeamplates.js'

const registerUser = async(req, res) => {
    const {name, usename, email} = req.body
    // User register code....
    const output = welcomeEmail(name, username)

    let mailOptions = {
        from: '"Welcome" <welcome@welcome.com>',
        to: 'client@gmail.com',
        subject: 'Welcome email!',
        text: 'Hello World',
        html: output,
    }
 

2
最佳答案 - Sehrish Waheed
1
最好的,简单易懂,感谢Cesar。 - Oussama Boumaad

6
如果你使用的是Nodemailer 2.0.0或更高版本,请查看以下文档:https://community.nodemailer.com/2-0-0-beta/templating/ 在那里,他们会解释如何使用外部渲染和类似这样的模板。
// external renderer
var EmailTemplate = require('email-templates').EmailTemplate;
var send = transporter.templateSender(new EmailTemplate('template/directory'));

他们还举了这样一个例子:
// create template based sender function
// assumes text.{ext} and html.{ext} in template/directory
var sendPwdReminder = transporter.templateSender(new EmailTemplate('template/directory'), {
    from: 'sender@example.com',
});

在这里,您可以了解如何传递变量。

您需要使用email-templates模块:https://github.com/crocodilejs/node-email-templates ,以及您选择的模板引擎。

此外,email-templates文档中还详细介绍了如何设置文件结构,以便找到您的模板:

html.{{ext}}(必需)-电子邮件的HTML格式

text.{{ext}}(可选)-电子邮件样式的文本格式。

{{ext}}(可选)-用于HTML格式主题的样式。

{{ext}}(可选)-电子邮件主题

请参阅支持的模板引擎,以了解可能用于上述{{ext}}值的模板引擎扩展名(例如.ejs,.jade,.nunjucks)。

您可以使用任何前缀来帮助您在IDE中更轻松地识别文件。唯一的要求是文件名分别包含html.,text.,style.和subject.。


有没有其他类似的方法,就像我们在.pug文件中所做的那样,只需将对象传递给映射变量? - Pardeep Jain

4

对于使用pug作为模板引擎的用户

使用pug的render函数快速将一个独立文件中的模板渲染出来的方法:

// function to send an e-mail. Assumes you've got nodemailer and pug templating engine installed. 
// transporter object relates to nodemailer, see nodemailer docs for details
const nodemailer = require('nodemailer');
const pug = require('pug');
function send_some_mail(iterable){
var message = {
  from: 'from@example.com',
  to: 'to@example.com',
  subject: 'Message title',
  html: pug.renderFile(__dirname + 'path_to_template.pug', {iterable: iterable})
};
transporter.sendMail(message, function(err, info){...})
}

// template.pug
each item in iterable
li
  p #{item.name}

请参见https://pugjs.org/api/getting-started.html了解更多详情。请注意,这将导致每次发送消息时重新编译模板。 对于偶尔的电子邮件投递,这是可以接受的。 如果您发送大量电子邮件,则可以缓存已编译的模板以解决此问题。如果需要,请查看pug文档进行设置。

3
你可以使用一个Web请求来构建一个HTML模板,使用handlebars或其他任何引擎。

创建模板

首先,您必须为电子邮件正文创建一个HTML模板。在此示例中,我使用了一个handlebars hbs文件。

enter image description here

使用HTML进行设计,并添加您在消息中需要的变量:

   <!DOCTYPE html>
   <html>
    <head>
        <meta name="viewport" content="width=device-width">
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
        <title>Welcome Email Template</title>
    </head>
    <body>
     <p style="font-size: 14px; font-weight: normal;">Hi {{data.name}}</p>
    </body>
   </html>

创建模板请求

您必须创建对此视图的访问权限。然后,我们可以将模板名称作为URL参数发送到创建的请求中,以使请求可参数化以用于其他模板。

const web = express.Router()

web.post('/template/email/:template', function(req, res) {
  res.render(`templates/email/${req.params.template}`, {
    data: req.body        
  })
})

邮件功能

在向模板请求后,最终你可以发送电子邮件。你可以使用以下函数:

const nodemailer = require('nodemailer')
const request = require("request")

function sendEmail(toEmail, subject, templateFile) {
    var options = {
        uri: `http://localhost:3000/template/email/${templateFile}`,
        method: 'POST',
        json: { name: "Jon Snow" } // All the information that needs to be sent
    };  
    request(options, function (error, response, body) {
        if (error) console.log(error)
        var transporter = nodemailer.createTransport({
            host: mailConfig.host,
            port: mailConfig.port,
            secure: true,
            auth: {
                user: mailConfig.account,
                pass: mailConfig.password
            }
        })
        var mailOptions = {
            from: mailConfig.account,
            to: toEmail,
            subject: subject,
            html: body
        }       
        transporter.sendMail(mailOptions, function(error, info) {
            if (error) console.log(error)
        })
    })
}

1
对于电子邮件而言,<!DOCTYPE html> 的长度要比这个长一些。 - Roko C. Buljan
请提供任何示例链接以查看此实现的代码框或CodePen,以便完整地查看其运行流程。 - Jatinder

2

这可以不使用模板实现。

尝试将其更改为:

`Hello ${username}!`

请确保这不是引号而是反引号。


1
现在这将输出为文本而非HTML。 - Abodesegun Ezekiel
这可能很危险,因为文本将不会进行HTML编码。 - Sam

2
在nodemailer中,有一种简单的方法可以在HTML中插入变量。
    html:"<p>Your message "+variable1+".Message continueous "+variable 2+"</p>"

1
确保不要在中间放置空格。 - ublec

0

您也可以使用async/await语法或者Promise来避免使用回调函数,就像我在这里使用的一样:

const fs = require("fs").promises;
const path = require("path");
const handlebars = require("handlebars");
const relativeTemplatePath = "../../html/reset-pw-email-template.html";

function sendEmail(){
    const templatePath = path.join(__dirname, relativeTemplatePath);
    const templateFile = await fs.readFile(templatePath, 'utf-8');
    const template = handlebars.compile(templateFile);
    const replacements = {
        username:""
    };
    const finalHtml = template(replacements);
    const mailOptions = {
       from: "",
       to: "",
       subject: "",
       html: finalHtml,
    };
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接