You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 23:05:36