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

如何通过Azure Communication Services用JS实现隐私批量发邮件及邮箱导入

Azure Communication Services 批量邮件发送解决方案

1. 实现收件人不可见的核心思路

要让收件人看不到其他收件人,绝对不能在同一个邮件的to数组中添加多个收件人,必须为每个收件人单独创建并发送一封邮件。这样每封邮件的收件人列表里只有单个用户,自然不会泄露其他收件人的信息。

2. 从文本/Excel文件导入收件人

文本文件导入(每行一个邮箱)

用Node.js内置的fs模块读取文本文件,按行分割提取有效邮箱地址。

Excel文件导入

使用xlsx库解析Excel文件,指定工作表和邮箱所在列提取数据,需要先安装依赖:npm install xlsx

3. 单独发送每封邮件的实现

遍历导入的收件人列表,逐个构造邮件对象并调用EmailClient.send方法。为避免并发请求过多触发频率限制,采用串行发送方式(也可根据需求调整并发数)。


完整实现代码

const { EmailClient } = require("@azure/communication-email");
const fs = require("fs");
const xlsx = require("xlsx");

// ACS配置信息
const connectionString = "endpoint=https://callandsms.communication.azure.com/;accesskey=xxxxxxxxxxxxxxxxxxxxxxx";
const sender = "support@xxxxx.azurecomm.net";
const emailContent = {
  subject: "ACS 批量邮件测试",
  plainText: "这是一封批量发送的测试邮件,您是唯一收件人。\n\n 测试内容:Azure Communication Services 批量邮件功能",
  html: "<html><head><title>ACS 批量邮件测试</title></head><body><h1>ACS 批量邮件测试</h1><p>这是一封批量发送的测试邮件,您是唯一收件人。</p></body></html>",
};

// 从文本文件读取收件人(每行一个邮箱)
function getRecipientsFromTxt(filePath) {
  const content = fs.readFileSync(filePath, "utf-8");
  return content.split("\n").filter(email => email.trim() !== "");
}

// 从Excel文件读取收件人(假设邮箱在第一列,工作表名为Sheet1)
function getRecipientsFromExcel(filePath) {
  const workbook = xlsx.readFile(filePath);
  const worksheet = workbook.Sheets["Sheet1"];
  const data = xlsx.utils.sheet_to_json(worksheet, { header: ["email"] });
  // 跳过表头(如果第一行是表头)
  return data.slice(1).map(item => item.email.trim()).filter(email => email !== "");
}

// 检查邮件发送状态
async function checkEmailStatus(client, messageId) {
  let counter = 0;
  return new Promise((resolve) => {
    const statusInterval = setInterval(async () => {
      counter++;
      try {
        const response = await client.getSendStatus(messageId);
        console.log(`邮件 ${messageId} 状态: [${response.status}]`);
        if (response.status.toLowerCase() !== "queued" || counter > 12) {
          clearInterval(statusInterval);
          resolve(response.status);
        }
      } catch (e) {
        console.log(`检查邮件 ${messageId} 状态失败:`, e);
        clearInterval(statusInterval);
        resolve("error");
      }
    }, 5000);
  });
}

// 发送单封邮件
async function sendSingleEmail(client, recipientEmail) {
  try {
    const emailMessage = {
      sender: sender,
      content: emailContent,
      recipients: {
        to: [{ email: recipientEmail, displayName: recipientEmail.split("@")[0] }]
      },
    };

    const sendResult = await client.send(emailMessage);
    if (sendResult && sendResult.messageId) {
      console.log(`发送邮件到 ${recipientEmail} 成功,MessageId: ${sendResult.messageId}`);
      await checkEmailStatus(client, sendResult.messageId);
      return true;
    } else {
      console.error(`发送邮件到 ${recipientEmail} 失败:`, sendResult);
      return false;
    }
  } catch (e) {
    console.log(`发送邮件到 ${recipientEmail} 时出错:`, e);
    return false;
  }
}

// 批量发送主函数
async function main() {
  const client = new EmailClient(connectionString);
  
  // 选择导入方式:文本文件或Excel
  // const recipients = getRecipientsFromTxt("./recipients.txt");
  const recipients = getRecipientsFromExcel("./recipients.xlsx");

  if (recipients.length === 0) {
    console.log("未找到有效收件人");
    return;
  }

  console.log(`开始发送 ${recipients.length} 封邮件...`);
  
  // 串行发送每封邮件,避免并发过高
  for (const recipient of recipients) {
    await sendSingleEmail(client, recipient);
    // 可选:每发送一封延迟1秒,规避频率限制
    await new Promise(resolve => setTimeout(resolve, 1000));
  }

  console.log("批量发送任务完成");
}

main();

代码关键说明

  • 收件人隔离:sendSingleEmail函数为每个收件人单独构造邮件,to数组仅包含单个用户,确保收件人看不到其他收件人信息。
  • 文件导入逻辑:
    • 文本文件:读取后按行分割,自动过滤空行和无效邮箱。
    • Excel文件:解析指定工作表,提取第一列的邮箱地址,自动跳过表头行。
  • 发送控制:采用for...of循环串行发送,添加可选延迟避免触发ACS的请求频率限制。
  • 状态监控:封装checkEmailStatus函数,异步跟踪每封邮件的发送状态,直到状态稳定或超时(60秒)。

内容的提问来源于stack exchange,提问作者Abderrahmane Abidi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 01:50:34