2023年如何在Node.js中通过Gmail API发送邮件?
2023年Node.js通过Gmail API发送邮件的实现方法
下面是针对个人/Workspace账号的完整实现步骤,亲测有效:
一、谷歌云平台前置配置
- 登录谷歌云控制台,创建新项目,搜索并启用Gmail API
- 进入「API和服务」→「凭据」,创建OAuth 2.0 客户端ID,应用类型选择「桌面应用」(服务端场景下,首次授权需要本地操作)
- 下载生成的
credentials.json,放到你的Node.js项目根目录 - 如果你是谷歌Workspace管理员,生产环境建议用服务账号+域范围委派,无需用户手动授权,直接通过服务账号代发
二、安装依赖
直接安装官方的谷歌API客户端包:
npm install googleapis mime-types
(mime-types用于自动识别附件的MIME类型,可选但推荐)
三、首次授权获取长期有效Token
个人账号需要先获取refresh token(用于自动刷新access token),运行以下脚本:
const { google } = require('googleapis'); const fs = require('fs'); const readline = require('readline'); const SCOPES = ['https://www.googleapis.com/auth/gmail.send']; const TOKEN_PATH = 'token.json'; fs.readFile('credentials.json', (err, content) => { if (err) return console.log('加载客户端凭证失败:', err); authorize(JSON.parse(content), getAccessToken); }); function authorize(credentials, callback) { const { client_secret, client_id, redirect_uris } = credentials.installed; const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]); fs.readFile(TOKEN_PATH, (err) => { if (err) return callback(oAuth2Client); console.log('Token已存在,无需重新授权'); }); } function getAccessToken(oAuth2Client) { const authUrl = oAuth2Client.generateAuthUrl({ access_type: 'offline', // 必须设置为offline才能获取refresh token scope: SCOPES, }); console.log('打开以下链接授权:', authUrl); const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); rl.question('输入页面上的授权码:', (code) => { rl.close(); oAuth2Client.getToken(code, (err, token) => { if (err) return console.error('获取Token失败:', err); oAuth2Client.setCredentials(token); fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => { if (err) return console.error('保存Token失败:', err); console.log('Token已保存到', TOKEN_PATH); }); }); }); }
运行脚本后,打开链接登录你的Gmail账号授权,复制授权码输入,会生成token.json文件。
四、核心发送邮件代码
封装一个可复用的发送函数,支持纯文本、HTML和附件:
const { google } = require('googleapis'); const fs = require('fs'); const mime = require('mime-types'); const SCOPES = ['https://www.googleapis.com/auth/gmail.send']; const TOKEN_PATH = 'token.json'; const CREDENTIALS_PATH = 'credentials.json'; // 获取授权客户端,自动刷新Token async function getAuthClient() { const credentials = JSON.parse(fs.readFileSync(CREDENTIALS_PATH)); const { client_secret, client_id, redirect_uris } = credentials.installed; const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]); // 加载已保存的Token const token = JSON.parse(fs.readFileSync(TOKEN_PATH)); oAuth2Client.setCredentials(token); // 自动刷新过期的Token if (oAuth2Client.isTokenExpiring()) { try { const newToken = await oAuth2Client.refreshAccessToken(); fs.writeFileSync(TOKEN_PATH, JSON.stringify(newToken.credentials)); oAuth2Client.setCredentials(newToken.credentials); } catch (err) { throw new Error('刷新Token失败:', err); } } return oAuth2Client; } // 发送邮件函数 async function sendEmail(to, subject, textBody, htmlBody = null, attachments = []) { const auth = await getAuthClient(); const gmail = google.gmail({ version: 'v1', auth }); // 构建MIME格式的邮件内容 const boundary = `boundary_${Date.now()}`; let emailParts = [ `To: ${to}`, `Subject: ${subject}`, 'MIME-Version: 1.0', `Content-Type: multipart/mixed; boundary="${boundary}"`, '', `--${boundary}`, 'Content-Type: text/plain; charset="UTF-8"', '', textBody ]; // 添加HTML内容(可选) if (htmlBody) { emailParts.push(`--${boundary}`); emailParts.push('Content-Type: text/html; charset="UTF-8"'); emailParts.push(''); emailParts.push(htmlBody); } // 添加附件(可选) for (const att of attachments) { const fileContent = fs.readFileSync(att.path); const base64Content = fileContent.toString('base64'); const mimeType = mime.lookup(att.path) || 'application/octet-stream'; emailParts.push(`--${boundary}`); emailParts.push(`Content-Type: ${mimeType}`); emailParts.push(`Content-Disposition: attachment; filename="${att.filename || att.path.split('/').pop()}"`); emailParts.push('Content-Transfer-Encoding: base64'); emailParts.push(''); emailParts.push(base64Content); } // 结束边界 emailParts.push(`--${boundary}--`); const email = emailParts.join('\n'); // 转换为Gmail要求的base64url格式 const encodedEmail = Buffer.from(email) .toString('base64') .replace(/\+/g, '-') .replace(/\//g, '_') .replace(/=+$/, ''); // 调用Gmail API发送邮件 const res = await gmail.users.messages.send({ userId: 'me', // 表示当前授权用户 requestBody: { raw: encodedEmail } }); console.log('邮件发送成功,ID:', res.data.id); return res.data; } // 调用示例 sendEmail( 'recipient@example.com', 'Gmail API测试邮件', '这是纯文本内容', '<h2>这是HTML格式的内容</h2><p>支持换行和样式</p>', [{ path: './test.pdf', filename: '测试文档.pdf' }] ).catch(err => console.error('邮件发送失败:', err));
五、关键注意事项
- 权限范围:只申请
gmail.send即可,不要用更大的权限,避免安全风险 - 个人账号限制:refresh token如果6个月未使用会被吊销,需要重新授权;生产环境优先用Workspace服务账号
- 配额限制:Gmail API有调用配额,个人账号每天约100封,Workspace账号配额更高,超出会被限流
- 邮件格式:必须严格遵循MIME规范,base64编码必须转成url安全格式(替换
+为-,/为_,去掉末尾的=)
内容的提问来源于stack exchange,提问作者coderNoob
相关产品推荐
相关产品推荐

