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

使用PHP mail函数发送2400封邮件遇阻,求分批发送方案

嘿,一次性发送2400封邮件确实容易踩坑——不管是Web服务器超时、主机商的邮件发送限制,还是被邮件服务商当成垃圾邮件拦截,分批发送绝对是更稳妥的选择。甚至结合队列系统会让整个流程更可控,我来给你拆解几种方案,顺便优化你的现有代码:

基础分批改造(快速见效)

如果你想快速修改现有代码实现分批发送,可以通过URL参数记录发送进度,每次请求处理固定数量的邮件(比如10封)。这样能避免单次请求超时,也降低被拦截的风险。

下面是修改后的代码,我做了这些优化:

  • 新增批次控制逻辑,通过offset参数记录已发送的位置
  • 修复了原代码中拼写错误的UFT-8编码(改为UTF-8)
  • 添加邮箱格式验证,跳过无效地址
  • 用更精细的usleep()代替sleep(),灵活控制发送间隔
  • 记录失败邮件,方便后续重试
<?php
include('../../includingThis.php');
$fra = "ME <me@me.com>";
$emne = "";
$sql = "SELECT * FROM sendThese WHERE id=1";
$til = [];
$message = "";
$besked = "";

if ($result = mysqli_query($con, $sql)) {
    while($row = mysqli_fetch_assoc($result)) {
        $emne = $row["subject"];
        $message = $row["message"];
        $mailsHere = $row["mailsHere"];
        $til = explode(",", $mailsHere);
    }
}

// 分批配置:每次发送10封
$batchSize = 10;
$offset = isset($_GET['offset']) ? (int)$_GET['offset'] : 0;
$totalMails = count($til);
$endIndex = min($offset + $batchSize, $totalMails);

$besked = file_get_contents('sendThese.html');
$besked = str_replace("%%message%%", $message, $besked);

// 修复编码拼写错误,添加标准换行符
$headers = "MIME-Version:1.0\n";
$headers .= "Content-type: text/html; charset=UTF-8\n";
$headers .= "From: ".$fra."\n";

$mailsSend = 0;
$failedMails = [];

// 处理当前批次的邮箱
for ($i = $offset; $i < $endIndex; $i++) {
    $email = trim($til[$i]);
    // 跳过无效邮箱格式
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $failedMails[] = $email . " (无效格式)";
        continue;
    }
    // 发送邮件(这里可以替换为PHPMailer更可靠)
    $sent = mail($email, $emne, $besked, $headers);
    if ($sent) {
        $mailsSend++;
        echo $email . " 发送成功<br>";
    } else {
        $failedMails[] = $email;
        echo $email . " 发送失败<br>";
    }
    // 间隔0.5秒,避免触发反垃圾机制(可根据服务商调整)
    usleep(500000);
}

// 输出批次结果
echo "<br>=== 批次总结 ===<br>";
echo "成功发送:" . $mailsSend . " 封<br>";
echo "发送失败:" . count($failedMails) . " 封<br>";
if (!empty($failedMails)) {
    echo "失败列表:<br>" . implode("<br>", $failedMails) . "<br>";
}

// 生成下一批次链接
if ($endIndex < $totalMails) {
    $nextOffset = $endIndex;
    $remaining = $totalMails - $endIndex;
    echo "<a href='?offset=" . $nextOffset . "'>点击发送下一批(剩余 " . $remaining . " 封)</a>";
} else {
    echo "<br>🎉 所有邮件处理完成!总共 " . $totalMails . " 封";
}

// 可选:将失败邮件存入数据库,方便后续重试
// if (!empty($failedMails)) {
//     $failedList = mysqli_real_escape_string($con, implode(",", $failedMails));
//     mysqli_query($con, "INSERT INTO failed_mails (emails, subject, created_at) VALUES ('$failedList', '$emne', NOW())");
// }
?>
进阶异步队列方案(专业可靠)

如果你的发送需求比较频繁,或者不想手动点击批次链接,推荐用CLI脚本+定时任务的异步方案。这种方式脱离Web服务器的超时限制,能自动后台处理邮件。

步骤1:修改数据库表

在sendThese表中新增sent_count字段(INT类型,默认0),用来记录已发送的邮件数量,避免重复发送。

步骤2:编写CLI处理脚本

<?php
include('../../includingThis.php');
$fra = "ME <me@me.com>";
$emne = "";
$sql = "SELECT * FROM sendThese WHERE id=1";
$til = [];
$message = "";
$besked = "";

if ($result = mysqli_query($con, $sql)) {
    while($row = mysqli_fetch_assoc($result)) {
        $emne = $row["subject"];
        $message = $row["message"];
        $mailsHere = $row["mailsHere"];
        $til = explode(",", $mailsHere);
        $sentCount = $row["sent_count"] ?? 0;
    }
}

$totalMails = count($til);
if ($sentCount >= $totalMails) {
    echo date('Y-m-d H:i:s') . " 所有邮件已发送完成!\n";
    exit;
}

// 每次处理10封
$batchSize = 10;
$startIndex = $sentCount;
$endIndex = min($startIndex + $batchSize, $totalMails);

$besked = file_get_contents('sendThese.html');
$besked = str_replace("%%message%%", $message, $besked);

$headers = "MIME-Version:1.0\n";
$headers .= "Content-type: text/html; charset=UTF-8\n";
$headers .= "From: ".$fra."\n";

$mailsSend = 0;
$failedMails = [];

for ($i = $startIndex; $i < $endIndex; $i++) {
    $email = trim($til[$i]);
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $failedMails[] = $email;
        continue;
    }
    $sent = mail($email, $emne, $besked, $headers);
    if ($sent) {
        $mailsSend++;
        echo date('Y-m-d H:i:s') . " 成功:" . $email . "\n";
    } else {
        $failedMails[] = $email;
        echo date('Y-m-d H:i:s') . " 失败:" . $email . "\n";
    }
    usleep(500000);
}

// 更新已发送数量到数据库
$newSentCount = $sentCount + $mailsSend;
mysqli_query($con, "UPDATE sendThese SET sent_count = $newSentCount WHERE id=1");

// 记录失败邮件
if (!empty($failedMails)) {
    $failedList = mysqli_real_escape_string($con, implode(",", $failedMails));
    mysqli_query($con, "INSERT INTO failed_mails (emails, subject, created_at) VALUES ('$failedList', '$emne', NOW())");
}

echo "\n本次处理完成:成功 " . $mailsSend . " 封,失败 " . count($failedMails) . " 封。剩余 " . ($totalMails - $newSentCount) . " 封\n";
?>

步骤3:设置定时任务(Cron)

在服务器上添加Cron任务,每分钟运行一次脚本,自动处理批次:

* * * * * php /path/to/your/mail_script.php >> /path/to/mail_logs/send.log 2>&1
关键优化细节
  • 替换原生mail()函数:推荐用PHPMailer或SwiftMailer,它们支持SMTP认证,错误处理更完善,还能避免很多原生函数的兼容性问题。
  • 遵守发送限制:不同邮件服务商有不同的发送频率限制(比如Gmail每天500封,每分钟100封),一定要调整间隔时间,避免被封号或拦截。
  • 添加退订机制:符合GDPR、CAN-SPAM等法规要求,减少被标记为垃圾邮件的概率。
  • 测试先行:先给少量测试邮箱发送,确认内容、格式和发送正常后,再启动批量发送。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 09:48:31