Node.js邮件发送服务搭建指南
Node.js 邮件发送服务搭建指南
一、准备工作
1.1 安装 Node.js
在搭建 Node.js 邮件发送服务之前,首先要确保系统中安装了 Node.js。Node.js 可以从其官方网站(https://nodejs.org/)下载对应操作系统的安装包进行安装。安装完成后,在命令行中输入 node -v
可以查看 Node.js 的版本号,以确认安装是否成功。
1.2 选择邮件发送库
在 Node.js 生态系统中,有多个库可以用于发送邮件,其中比较常用的是 nodemailer
。nodemailer
是一个功能丰富且易于使用的邮件发送库,支持多种传输方式,如 SMTP、SES 等。可以通过 npm(Node Package Manager)来安装 nodemailer
。在项目目录下打开命令行,执行以下命令:
npm install nodemailer
二、基本邮件发送
2.1 创建简单的邮件发送脚本
安装好 nodemailer
后,就可以编写一个简单的邮件发送脚本。在项目目录下创建一个新的 JavaScript 文件,例如 sendMail.js
。
const nodemailer = require('nodemailer');
// 创建一个传输对象
let transporter = nodemailer.createTransport({
host: 'smtp.qq.com', // 以 QQ 邮箱 SMTP 服务器为例
port: 465,
secure: true, // 使用 SSL 连接
auth: {
user: 'your_email@qq.com', // 你的邮箱地址
pass: 'your_email_password' // 你的邮箱密码(或授权码,对于某些邮箱如 QQ 邮箱需要使用授权码)
}
});
// 邮件内容
let mailOptions = {
from: 'your_email@qq.com', // 发件人邮箱
to: 'recipient_email@example.com', // 收件人邮箱
subject: 'Node.js 邮件测试', // 邮件主题
text: '这是一封通过 Node.js 发送的测试邮件' // 邮件正文纯文本内容
};
// 发送邮件
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('邮件已发送: ', info.response);
});
在上述代码中:
- 首先引入了
nodemailer
模块。 - 使用
createTransport
方法创建了一个传输对象,配置了 SMTP 服务器的相关信息,包括服务器地址、端口、是否使用 SSL 以及认证信息。这里以 QQ 邮箱为例,不同邮箱的 SMTP 服务器设置会有所不同。 - 定义了
mailOptions
对象,包含了邮件的发件人、收件人、主题和正文等信息。 - 最后使用
transporter.sendMail
方法发送邮件,并在回调函数中处理发送过程中的错误和成功响应。
2.2 配置不同邮箱的 SMTP 设置
2.2.1 QQ 邮箱
QQ 邮箱的 SMTP 服务器地址为 smtp.qq.com
,端口为 465(SSL)或 587(TLS)。由于 QQ 邮箱出于安全考虑,需要开启 SMTP 服务并获取授权码来代替密码进行登录。具体步骤如下:
- 登录 QQ 邮箱,点击设置 - 账户。
- 在账户设置页面中,找到“POP3/IMAP/SMTP/Exchange/CardDAV/CalDAV 服务”,开启“SMTP 服务”。
- 按照提示通过短信验证等方式获取授权码,在代码中使用授权码代替邮箱密码。
2.2.2 Gmail 邮箱
Gmail 的 SMTP 服务器地址为 smtp.gmail.com
,端口为 465(SSL)或 587(TLS)。如果使用 Gmail 发送邮件,需要注意 Google 对于安全性的要求。有时候可能需要在 Google 账户设置中允许“不太安全的应用访问”(不推荐长期使用此设置,存在安全风险),或者通过 OAuth 2.0 进行身份验证。
以下是使用 Gmail SMTP 服务器且使用 OAuth 2.0 认证的示例代码(此示例较为复杂,需要先获取 OAuth 2.0 相关的凭证):
const {google} = require('googleapis');
const nodemailer = require('nodemailer');
const OAuth2 = google.auth.OAuth2;
// 配置 OAuth 2.0 凭证
const oauth2Client = new OAuth2(
'YOUR_CLIENT_ID',
'YOUR_CLIENT_SECRET',
'https://developers.google.com/oauthplayground'
);
oauth2Client.setCredentials({
refresh_token: 'YOUR_REFRESH_TOKEN'
});
// 获取访问令牌
const accessToken = new Promise((resolve, reject) => {
oauth2Client.getAccessToken((err, token) => {
if (err) {
reject(err);
} else {
resolve(token);
}
});
});
// 创建传输对象
const createTransporter = async () => {
const token = await accessToken;
return nodemailer.createTransport({
host: 'smtp.gmail.com',
port: 465,
secure: true,
auth: {
type: 'OAuth2',
user: 'your_email@gmail.com',
clientId: 'YOUR_CLIENT_ID',
clientSecret: 'YOUR_CLIENT_SECRET',
refreshToken: 'YOUR_REFRESH_TOKEN',
accessToken: token
}
});
};
// 邮件内容
const mailOptions = {
from: 'your_email@gmail.com',
to:'recipient_email@example.com',
subject: 'Node.js Gmail 邮件测试',
text: '这是一封通过 Node.js 使用 Gmail 发送的测试邮件'
};
// 发送邮件
createTransporter().then(transporter => {
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('邮件已发送: ', info.response);
});
}).catch(console.error);
2.2.3 163 邮箱
163 邮箱的 SMTP 服务器地址为 smtp.163.com
,端口为 465(SSL)或 25(非加密)。同样,需要在 163 邮箱设置中开启 SMTP 服务,并获取授权码用于登录。在代码中的配置与 QQ 邮箱类似,只需将服务器地址和认证信息修改为 163 邮箱对应的信息即可。
const nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
host:'smtp.163.com',
port: 465,
secure: true,
auth: {
user: 'your_email@163.com',
pass: 'your_email_password_or_auth_code'
}
});
let mailOptions = {
from: 'your_email@163.com',
to:'recipient_email@example.com',
subject: 'Node.js 163 邮箱邮件测试',
text: '这是一封通过 Node.js 使用 163 邮箱发送的测试邮件'
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('邮件已发送: ', info.response);
});
三、邮件内容丰富化
3.1 添加 HTML 内容
除了纯文本内容,邮件还可以包含 HTML 格式的正文,这样可以使邮件更加美观和丰富。在 mailOptions
中添加 html
字段即可。
const nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
host:'smtp.qq.com',
port: 465,
secure: true,
auth: {
user: 'your_email@qq.com',
pass: 'your_email_password'
}
});
let mailOptions = {
from: 'your_email@qq.com',
to:'recipient_email@example.com',
subject: 'Node.js HTML 邮件测试',
text: '这是纯文本内容,不支持 HTML 格式',
html: '<h1>这是一封 HTML 格式的邮件</h1><p>这里可以包含各种 HTML 标签,如链接:<a href="https://example.com">示例链接</a></p>'
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('邮件已发送: ', info.response);
});
在上述代码中,html
字段包含了 HTML 格式的内容,邮件客户端在显示邮件时会渲染这些 HTML 代码。
3.2 添加附件
可以通过在 mailOptions
中添加 attachments
字段来给邮件添加附件。attachments
是一个数组,每个元素代表一个附件。
const nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
host:'smtp.qq.com',
port: 465,
secure: true,
auth: {
user: 'your_email@qq.com',
pass: 'your_email_password'
}
});
let mailOptions = {
from: 'your_email@qq.com',
to:'recipient_email@example.com',
subject: 'Node.js 带附件邮件测试',
text: '这是一封带有附件的邮件',
attachments: [
{
filename: 'example.txt',
path: './example.txt'
},
{
filename: 'image.jpg',
path: './image.jpg',
contentType: 'image/jpeg'
}
]
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('邮件已发送: ', info.response);
});
在上述代码中,attachments
数组中有两个附件,一个是文本文件 example.txt
,另一个是图片文件 image.jpg
。filename
表示附件在邮件中显示的文件名,path
表示附件在本地文件系统中的路径,contentType
用于指定附件的 MIME 类型,对于一些常见文件类型,nodemailer
可以自动推断,但对于一些特殊类型建议手动指定。
3.3 嵌入图片
在 HTML 邮件中嵌入图片可以使邮件更加生动。可以通过将图片作为附件添加,并在 HTML 中使用 cid
(Content - ID)来引用。
const nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
host:'smtp.qq.com',
port: 465,
secure: true,
auth: {
user: 'your_email@qq.com',
pass: 'your_email_password'
}
});
let mailOptions = {
from: 'your_email@qq.com',
to:'recipient_email@example.com',
subject: 'Node.js 嵌入图片邮件测试',
text: '这是一封嵌入图片的邮件',
html: '<h1>嵌入图片示例</h1><img src="cid:logo" alt="公司 logo">',
attachments: [
{
filename: 'logo.png',
path: './logo.png',
cid: 'logo' // 与 HTML 中引用的 cid 一致
}
]
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('邮件已发送: ', info.response);
});
在上述代码中,首先在 attachments
中添加了一个图片附件,并指定了 cid
为 logo
。然后在 html
内容中通过 <img src="cid:logo">
来引用该图片,这样图片就会嵌入到 HTML 邮件中显示。
四、邮件发送优化与错误处理
4.1 连接池与性能优化
在高并发场景下,频繁创建和销毁邮件传输连接会影响性能。nodemailer
支持连接池功能,可以复用连接来提高性能。通过在 createTransport
的配置中设置 pool: true
来启用连接池。
const nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
host:'smtp.qq.com',
port: 465,
secure: true,
auth: {
user: 'your_email@qq.com',
pass: 'your_email_password'
},
pool: true,
maxConnections: 5 // 最大连接数
});
// 多个邮件发送任务
const mailOptionsArray = [
{
from: 'your_email@qq.com',
to:'recipient1_email@example.com',
subject: '邮件 1',
text: '邮件 1 的内容'
},
{
from: 'your_email@qq.com',
to:'recipient2_email@example.com',
subject: '邮件 2',
text: '邮件 2 的内容'
}
];
mailOptionsArray.forEach(mailOptions => {
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return console.log(error);
}
console.log('邮件已发送: ', info.response);
});
});
在上述代码中,启用了连接池并设置了最大连接数为 5。这样在处理多个邮件发送任务时,nodemailer
会复用连接,而不是每次都创建新的连接,从而提高了性能。
4.2 全面的错误处理
在邮件发送过程中,可能会遇到各种错误,如认证失败、网络问题、SMTP 服务器错误等。需要对这些错误进行全面的处理,以提供更好的用户体验和系统稳定性。
const nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
host:'smtp.qq.com',
port: 465,
secure: true,
auth: {
user: 'your_email@qq.com',
pass: 'your_email_password'
}
});
let mailOptions = {
from: 'your_email@qq.com',
to:'recipient_email@example.com',
subject: '错误处理测试邮件',
text: '这是一封测试错误处理的邮件'
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
if (error.code === 'ETIMEDOUT') {
console.log('连接超时,可能是网络问题或服务器响应慢');
} else if (error.code === 'EAUTH') {
console.log('认证失败,检查邮箱账号和密码(或授权码)');
} else {
console.log('其他错误: ', error.message);
}
} else {
console.log('邮件已发送: ', info.response);
}
});
在上述代码中,对常见的错误码进行了针对性的处理。例如,ETIMEDOUT
错误表示连接超时,可能是网络问题或服务器响应慢;EAUTH
错误表示认证失败,需要检查邮箱账号和密码(或授权码)。对于其他未知错误,也打印出了错误信息以便排查问题。
五、集成到 Web 应用
5.1 在 Express 应用中发送邮件
如果使用 Express 框架来构建 Web 应用,可以很方便地将邮件发送功能集成进去。以下是一个简单的示例,在 Express 应用中通过一个 API 接口来发送邮件。
首先确保已经安装了 Express:
npm install express
然后创建一个 Express 应用并添加邮件发送功能:
const express = require('express');
const nodemailer = require('nodemailer');
const app = express();
const port = 3000;
let transporter = nodemailer.createTransport({
host:'smtp.qq.com',
port: 465,
secure: true,
auth: {
user: 'your_email@qq.com',
pass: 'your_email_password'
}
});
app.post('/send-mail', (req, res) => {
let mailOptions = {
from: 'your_email@qq.com',
to: req.body.to,
subject: req.body.subject,
text: req.body.text
};
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return res.status(500).json({error: '邮件发送失败', details: error.message});
}
res.status(200).json({message: '邮件已成功发送', response: info.response});
});
});
app.listen(port, () => {
console.log(`服务器在端口 ${port} 上运行`);
});
在上述代码中:
- 引入了 Express 和
nodemailer
模块。 - 创建了一个 Express 应用,并定义了一个 POST 路由
/send - mail
。 - 在路由处理函数中,从请求体中获取收件人、主题和正文等信息,构建邮件选项并发送邮件。
- 根据邮件发送的结果返回相应的 HTTP 状态码和 JSON 格式的响应。
5.2 在其他框架或服务中集成
除了 Express,nodemailer
也可以很方便地集成到其他 Node.js Web 框架中,如 Koa、Hapi 等。集成的思路基本相同,都是在相应的路由或业务逻辑中调用 nodemailer
的邮件发送方法。
例如在 Koa 框架中:
首先安装 Koa:
npm install koa
然后创建一个 Koa 应用并集成邮件发送功能:
const Koa = require('koa');
const nodemailer = require('nodemailer');
const app = new Koa();
let transporter = nodemailer.createTransport({
host:'smtp.qq.com',
port: 465,
secure: true,
auth: {
user: 'your_email@qq.com',
pass: 'your_email_password'
}
});
app.use(async (ctx) => {
if (ctx.path === '/send-mail' && ctx.method === 'POST') {
const {to, subject, text} = ctx.request.body;
let mailOptions = {
from: 'your_email@qq.com',
to,
subject,
text
};
try {
const info = await transporter.sendMail(mailOptions);
ctx.status = 200;
ctx.body = {message: '邮件已成功发送', response: info.response};
} catch (error) {
ctx.status = 500;
ctx.body = {error: '邮件发送失败', details: error.message};
}
}
});
app.listen(3000, () => {
console.log('服务器在端口 3000 上运行');
});
在 Koa 应用中,通过 app.use
中间件来处理 /send - mail
的 POST 请求,同样从请求体中获取邮件相关信息并发送邮件,根据结果返回不同的响应。
六、安全考虑
6.1 保护认证信息
在代码中直接硬编码邮箱账号和密码(或授权码)是非常不安全的做法。可以使用环境变量来存储这些敏感信息。在 Node.js 中,可以通过 process.env
来访问环境变量。
例如,在 .bashrc
(对于 Linux 和 macOS)或 .bash_profile
文件中设置环境变量:
export EMAIL_USER='your_email@qq.com'
export EMAIL_PASSWORD='your_email_password'
然后在 Node.js 代码中:
const nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
host:'smtp.qq.com',
port: 465,
secure: true,
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASSWORD
}
});
这样,敏感的认证信息不会暴露在代码仓库中,提高了安全性。
6.2 防止邮件被标记为垃圾邮件
为了防止发送的邮件被收件人的邮件服务器标记为垃圾邮件,可以采取以下措施:
- 使用正确的邮件头:确保
from
、reply - to
等邮件头信息准确且合理。from
字段应该是一个有效的邮箱地址,并且最好与域名相关联。 - 避免使用垃圾词汇:在邮件主题和正文中避免使用常见的垃圾邮件词汇,如“免费”“快速致富”等。
- 遵循最佳实践:如提供退订链接(对于营销邮件)、不要过度发送邮件等。
6.3 加密传输
始终使用加密的连接方式(如 SSL/TLS)来与 SMTP 服务器通信。在 nodemailer
的配置中,通过设置 secure: true
(对于 SSL)或 tls: {rejectUnauthorized: false}
(对于 TLS,同时需要注意安全性)来确保加密传输。这样可以防止邮件内容在传输过程中被窃取或篡改。
const nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
host:'smtp.qq.com',
port: 465,
secure: true, // 使用 SSL 连接
auth: {
user: 'your_email@qq.com',
pass: 'your_email_password'
}
});
或者对于 TLS:
const nodemailer = require('nodemailer');
let transporter = nodemailer.createTransport({
host:'smtp.qq.com',
port: 587,
secure: false,
tls: {
rejectUnauthorized: false
},
auth: {
user: 'your_email@qq.com',
pass: 'your_email_password'
}
});
不过,对于 rejectUnauthorized: false
的设置要谨慎使用,因为它会忽略服务器证书的验证,存在一定的安全风险,只建议在测试环境或对服务器证书有充分信任的情况下使用。在生产环境中,最好确保服务器证书是有效的且被正确验证。