前言
邮件发送功能是很多应用程序的必备功能(尤其是那些需要用户注册、密码重置或通知系统的应用)。在 Node.js 生态系统中,Nodemailer 无疑是最受欢迎、最可靠的邮件发送库!!!它简单易用,功能又足够强大,让开发者能够快速集成邮件功能到自己的应用中。
我第一次使用 Nodemailer 是在构建一个需要发送注册确认邮件的网站时。当时被它的简洁API和丰富的功能所震撼。今天就让我带你一起探索这个强大的工具,从零开始学习如何使用 Nodemailer 发送各种类型的邮件。
Nodemailer 是什么?
Nodemailer 是一个专为 Node.js 设计的邮件发送模块,由 Andris Reinman 创建于2010年。多年来,它已经成为 Node.js 社区中发送邮件的标准解决方案。它支持从简单的纯文本邮件到复杂的 HTML 内容,甚至可以添加附件和嵌入图片。
主要特点包括:
- 易于使用的 API
- Unicode 支持
- HTML 内容支持
- 附件支持(包括数据流)
- 安全的邮件发送(TLS/SSL)
- 支持各种邮件服务提供商
- 可自定义邮件头
- 支持 SMTP 和其他传输方式
安装 Nodemailer
在开始之前,确保你已经安装了 Node.js 环境。然后,在你的项目目录中运行以下命令:
npm install nodemailer
就这么简单!一行命令就把 Nodemailer 添加到你的项目中了。
基础使用:发送一封简单的邮件
让我们从最基础的开始 - 发送一封简单的文本邮件。
const nodemailer = require('nodemailer');
// 创建一个SMTP传输对象
const transporter = nodemailer.createTransport({
service: 'gmail', // 使用预定义的服务
auth: {
user: 'your.email@gmail.***',
pass: 'your-password-or-app-password'
}
});
// 定义邮件选项
const mailOptions = {
from: 'your.email@gmail.***',
to: 'recipient@example.***',
subject: '你好!这是一封测试邮件',
text: '如果你能看到这个消息,说明Nodemailer运行正常!'
};
// 发送邮件
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.error('发送失败:', error);
}
console.log('邮件已发送:', info.response);
});
这段代码做了几件事:
- 导入 Nodemailer 模块
- 创建一个传输对象(配置发送邮件的服务器信息)
- 定义邮件内容(发件人、收件人、主题和正文)
- 使用
sendMail方法发送邮件
不过等等!上面的例子使用了 Gmail 作为邮件服务,如果你真的要用 Gmail,可能会遇到一些问题(特别是安全设置)。Gmail 现在默认不允许"不安全应用程序"访问,所以你需要生成"应用专用密码"或者调整设置。
使用其他邮件服务提供商
除了 Gmail,Nodemailer 还支持多种邮件服务提供商。你可以使用 SMTP 配置来连接几乎任何邮件服务:
const transporter = nodemailer.createTransport({
host: 'smtp.example.***',
port: 587,
secure: false, // true用于465端口,false用于其他端口
auth: {
user: 'username@example.***',
pass: 'password'
}
});
常见邮件服务的配置:
-
Outlook/Hotmail:
host: 'smtp-mail.outlook.***', port: 587, secure: false -
QQ邮箱:
host: 'smtp.qq.***', port: 465, secure: true -
163邮箱:
host: 'smtp.163.***', port: 465, secure: true
发送HTML格式邮件
纯文本邮件有点单调,对吧?让我们升级一下,发送HTML格式的邮件:
const mailOptions = {
from: 'your.email@example.***',
to: 'recipient@example.***',
subject: '漂亮的HTML邮件',
html: `
<h1>欢迎使用Nodemailer!</h1>
<p>这是一封<b>HTML格式</b>的邮件,你可以添加:</p>
<ul>
<li>标题</li>
<li>列表</li>
<li>甚至是<a href="https://example.***">链接</a></li>
</ul>
<p>是不是很酷?</p>
`
};
使用 html 属性而不是 text 属性,就可以发送HTML格式的邮件了。当然,你也可以同时设置两者,这样在不支持HTML的邮件客户端中会显示纯文本内容。
添加附件
需要发送文件?Nodemailer 让添加附件变得超级简单:
const mailOptions = {
from: 'your.email@example.***',
to: 'recipient@example.***',
subject: '带附件的邮件',
text: '请查看附件!',
attachments: [
{
filename: '文档.pdf',
path: '/path/to/document.pdf'
},
{
filename: '图片.jpg',
path: '/path/to/image.jpg'
}
]
};
你甚至可以直接使用Buffer或Stream作为附件:
attachments: [
{
filename: '生成的文本.txt',
content: Buffer.from('这是一个动态生成的文本文件')
},
{
filename: '网络图片.jpg',
path: 'https://example.***/some-image.jpg'
}
]
发送给多个收件人
要发送给多个收件人,只需在 to 字段中用逗号分隔邮箱地址:
const mailOptions = {
from: 'your.email@example.***',
to: 'recipient1@example.***, recipient2@example.***, recipient3@example.***',
subject: '群发邮件',
text: '这封邮件发给了多个人!'
};
如果你想使用抄送(***)或密送(B***)功能:
const mailOptions = {
from: 'your.email@example.***',
to: 'main-recipient@example.***',
***: 'copy-recipient@example.***',
b***: 'hidden-recipient@example.***',
subject: '使用***和B***',
text: '主收件人可以看到***,但看不到B***的收件人。'
};
在HTML邮件中嵌入图片
想在HTML邮件中嵌入图片,而不是作为附件发送?Nodemailer也能轻松实现:
const mailOptions = {
from: 'your.email@example.***',
to: 'recipient@example.***',
subject: '带嵌入图片的邮件',
html: `
<h1>看看这张漂亮的图片!</h1>
<img src="cid:unique-image-id">
`,
attachments: [
{
filename: 'image.jpg',
path: '/path/to/image.jpg',
cid: 'unique-image-id' // 与HTML中的src匹配
}
]
};
关键是使用 cid (Content ID) 属性,HTML中的图片引用这个ID,而不是外部URL。
使用模板引擎
对于复杂的邮件内容,手写HTML不是最佳选择。更好的方法是使用模板引擎,比如 EJS、Handlebars 或 Pug。
以 EJS 为例:
- 安装 EJS:
npm install ejs
- 创建一个邮件模板 (email-template.ejs):
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>邮件通知</title>
</head>
<body>
<h1>你好,<%= name %>!</h1>
<p>感谢你注册我们的服务。</p>
<% if (verificationRequired) { %>
<p>请点击下面的链接验证你的邮箱:</p>
<a href="<%= verificationLink %>">验证邮箱</a>
<% } %>
<p>祝好,<br>团队</p>
</body>
</html>
- 在代码中使用模板:
const ejs = require('ejs');
const path = require('path');
// 渲染邮件模板
ejs.renderFile(
path.join(__dirname, 'email-template.ejs'),
{
name: '张三',
verificationRequired: true,
verificationLink: 'https://example.***/verify?token=abc123'
},
(err, html) => {
if (err) {
console.error('模板渲染错误:', err);
return;
}
// 使用渲染后的HTML发送邮件
const mailOptions = {
from: 'your.email@example.***',
to: 'recipient@example.***',
subject: '欢迎注册',
html: html
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.error('发送失败:', error);
}
console.log('邮件已发送:', info.response);
});
}
);
这种方式可以让你的邮件内容更加动态和可维护。
处理不同的邮件场景
让我们看看几个常见的邮件发送场景:
1. 欢迎邮件
function sendWel***eEmail(user) {
const mailOptions = {
from: 'your-app@example.***',
to: user.email,
subject: `欢迎加入,${user.name}!`,
html: `
<h1>欢迎来到我们的平台!</h1>
<p>亲爱的 ${user.name},</p>
<p>感谢你注册我们的服务。我们很高兴有你的加入!</p>
<p>如果你有任何问题,可以直接回复这封邮件。</p>
<p>祝你使用愉快!</p>
`
};
return transporter.sendMail(mailOptions);
}
2. 密码重置邮件
function sendPasswordResetEmail(user, resetToken) {
const resetUrl = `https://yourapp.***/reset-password?token=${resetToken}`;
const mailOptions = {
from: 'security@yourapp.***',
to: user.email,
subject: '密码重置请求',
html: `
<h1>密码重置</h1>
<p>亲爱的用户,</p>
<p>我们收到了你的密码重置请求。如果这不是你发起的,请忽略此邮件。</p>
<p>点击下面的链接重置密码(链接24小时内有效):</p>
<a href="${resetUrl}">重置密码</a>
<p>出于安全原因,此链接将在24小时后失效。</p>
`
};
return transporter.sendMail(mailOptions);
}
3. 订单确认邮件
function sendOrderConfirmation(order, user) {
// 生成订单项目的HTML
const itemsHtml = order.items.map(item => `
<tr>
<td>${item.name}</td>
<td>${item.quantity}</td>
<td>¥${item.price.toFixed(2)}</td>
<td>¥${(item.price * item.quantity).toFixed(2)}</td>
</tr>
`).join('');
const mailOptions = {
from: 'orders@yourstore.***',
to: user.email,
subject: `订单 #${order.id} 确认`,
html: `
<h1>感谢你的订购!</h1>
<p>亲爱的 ${user.name},</p>
<p>我们已收到你的订单(订单号:${order.id})。</p>
<h2>订单详情:</h2>
<table border="1" cellpadding="5" cellspacing="0">
<tr>
<th>商品</th>
<th>数量</th>
<th>单价</th>
<th>小计</th>
</tr>
${itemsHtml}
<tr>
<td colspan="3" align="right"><strong>总计:</strong></td>
<td>¥${order.total.toFixed(2)}</td>
</tr>
</table>
<p>预计发货时间:${order.estimatedShippingDate}</p>
<p>如有问题,请联系我们的客服团队。</p>
`
};
return transporter.sendMail(mailOptions);
}
使用Promise和Async/Await
Nodemailer的sendMail方法支持回调和Promise。如果你喜欢更现代的异步代码风格,可以这样使用:
// 使用Promise
transporter.sendMail(mailOptions)
.then(info => {
console.log('邮件已发送:', info.response);
})
.catch(error => {
console.error('发送失败:', error);
});
// 或者使用async/await
async function sendEmail() {
try {
const info = await transporter.sendMail(mailOptions);
console.log('邮件已发送:', info.response);
return info;
} catch (error) {
console.error('发送失败:', error);
throw error;
}
}
测试邮件发送
在开发阶段,你可能不想真的发送邮件。Nodemailer提供了一个很棒的测试工具 - Ethereal Email。
async function main() {
// 创建测试账户
const testA***ount = await nodemailer.createTestA***ount();
// 创建测试用的传输对象
const transporter = nodemailer.createTransport({
host: 'smtp.ethereal.email',
port: 587,
secure: false,
auth: {
user: testA***ount.user,
pass: testA***ount.pass
}
});
// 发送邮件
const info = await transporter.sendMail({
from: '"测试发件人" <test@example.***>',
to: 'recipient@example.***',
subject: '测试邮件',
text: '这是一封测试邮件',
html: '<b>这是一封测试邮件</b>'
});
console.log('邮件已发送: %s', info.messageId);
// 预览URL (仅适用于Ethereal邮箱)
console.log('预览URL: %s', nodemailer.getTestMessageUrl(info));
}
main().catch(console.error);
这样,你就可以在不实际发送邮件的情况下测试你的邮件功能。Ethereal会捕获这些邮件,并提供一个网页链接让你查看邮件的样子。
处理常见问题
认证失败
如果遇到"Authentication failed"错误:
- 检查用户名和密码是否正确
- 对于Gmail,确保启用了"低安全性应用访问"或使用应用专用密码
- 确认你的邮件服务提供商没有封锁SMTP访问
发送速率限制
大多数邮件服务提供商都有发送速率限制。如果你需要发送大量邮件,考虑:
- 实现队列系统,控制发送速率
- 使用专业的邮件发送服务,如SendGrid、Mailgun或Amazon SES
// 简单的邮件队列示例
const emailQueue = [];
let isSending = false;
function addToQueue(mailOptions) {
emailQueue.push(mailOptions);
if (!isSending) {
processQueue();
}
}
async function processQueue() {
if (emailQueue.length === 0) {
isSending = false;
return;
}
isSending = true;
const mail = emailQueue.shift();
try {
await transporter.sendMail(mail);
console.log('邮件已发送');
} catch (error) {
console.error('发送失败:', error);
}
// 延迟一秒后发送下一封,避免超过发送限制
setTimeout(processQueue, 1000);
}
邮件被标记为垃圾邮件
为减少被标记为垃圾邮件的可能性:
- 使用SPF和DKIM验证(需要域名DNS配置)
- 避免使用过多的感叹号和全大写单词
- 确保HTML和文本内容都存在
- 不要发送未经请求的邮件
- 提供明确的退订选项
结语
Nodemailer 是一个功能强大又易于使用的邮件发送库,它极大地简化了在 Node.js 应用中集成邮件功能的过程。从简单的文本邮件到复杂的 HTML 邮件,从单个收件人到批量发送,Nodemailer 都能应对自如。
希望这篇入门指南能帮助你快速上手 Nodemailer!随着你的深入使用,你会发现它还有更多高级功能可以探索,比如事件处理、插件系统等。
记住,好的邮件不仅仅是技术问题 - 内容、时机和相关性同样重要。无论你是用它来发送注册确认、密码重置链接,还是营销通讯,都要确保你的邮件是用户期望收到的,并提供真正的价值。
祝你编码愉快!