如何使用Nodemailer发送外部HTML文件替代JS内嵌HTML代码?
Nodemailer 引用外部HTML文件发送邮件实现方案
你可以通过两种方式实现需求,无需在JS文件中内嵌大段HTML代码:
方案1:原生Node.js实现(无额外依赖)
不需要安装第三方包,直接用Node内置模块即可完成:
- 第一步:拆分独立模板文件
在项目中新建模板存放目录,比如templates,将原来内嵌的HTML代码单独存为resetPasswordEmail.html,动态内容用自定义占位符标记:<!-- templates/resetPasswordEmail.html --> <html> <head> <meta charset="utf-8" /> <style> body, * { direction: ltr; } </style> </head> <body> <h1>You've requested a password reset!</h1> <h3> Click this <a href="{{RESET_LINK}}">Link</a> to set a new password. </h3> <body> </html> - 第二步:修改业务代码,读取模板替换动态内容
引入Node内置的文件系统和路径处理模块,读取外部HTML文件后替换占位符,再传入mailOptions即可:// 文件顶部引入内置模块 const fs = require('fs/promises'); const path = require('path'); // 原有业务逻辑修改 .then(async user => { // 拼接模板文件绝对路径,避免相对路径读取失败 const templatePath = path.resolve(__dirname, 'templates', 'resetPasswordEmail.html'); let htmlContent = await fs.readFile(templatePath, 'utf-8'); // 替换模板中的动态占位符 const resetLink = `http://localhost:8000/new-password/${token}`; htmlContent = htmlContent.replace('{{RESET_LINK}}', resetLink); const mailOptions = { from: "Mohammad.front.dev@gmail.com", to: user.email, subject: "Reset Password", html: htmlContent }; transporter.sendMail(mailOptions, (error, info) => { if(error) { console.log(error); req.flash("error", "重置邮件发送失败,请稍后重试"); } else { console.log(info); req.flash("info", "请查收邮件中的密码重置链接"); } res.redirect("/products"); }); })注:原代码先执行跳转再发邮件的逻辑存在体验问题,如果邮件发送失败用户无法收到错误提示,建议将响应逻辑放到邮件发送回调中处理。
方案2:使用模板引擎实现(适合多动态变量场景)
如果邮件模板中动态内容较多,手动替换占位符维护成本高,可以用模板引擎渲染,以常用的EJS为例:
- 安装依赖
npm install ejs - 编写EJS格式模板,将动态内容用EJS语法标记,存为
templates/resetPasswordEmail.ejs,只需要把原HTML中的占位符{{RESET_LINK}}替换为<%= resetLink %>即可。 - 调用渲染方法传入参数生成最终HTML:
const ejs = require('ejs'); const path = require('path'); .then(async user => { const templatePath = path.resolve(__dirname, 'templates', 'resetPasswordEmail.ejs'); // 直接传入动态参数渲染模板 const htmlContent = await ejs.renderFile(templatePath, { resetLink: `http://localhost:8000/new-password/${token}` }); const mailOptions = { from: "Mohammad.front.dev@gmail.com", to: user.email, subject: "Reset Password", html: htmlContent }; // 后续邮件发送、响应逻辑和方案1一致 transporter.sendMail(mailOptions, (error, info) => { // 错误处理、消息提示、跳转 }); })
注意事项
- 读取模板文件时不要直接写相对路径,必须用
path.resolve结合__dirname拼接绝对路径,避免启动目录变化、部署时出现文件找不到的问题 - 生产环境可以在服务启动时一次性读取模板文件缓存到内存中,不需要每次请求都读磁盘,提升响应速度
- 多数邮箱客户端对head标签内的CSS兼容性较差,正式发送的通知邮件建议把样式写成内联形式,避免样式丢失
内容的提问来源于stack exchange,提问作者MORA
相关产品推荐
相关产品推荐

