
本文详解 php 手动构造 multipart mime 邮件时的常见陷阱,重点解决 apple mail 显示空白、仅显示最后一张图等问题,通过嵌套 multipart/related + multipart/alternative 结构实现跨平台(ios/android/web)稳定渲染。
Apple Mail 对 MIME 结构的解析比大多数客户端更严格——它不支持在 multipart/alternative 中直接混排图像部分与 HTML 部分,也不接受 Content-ID 引用但未被正确包裹在 multipart/related 子边界内的内联图片。你原始代码的问题根源在于:单一层级的 multipart/alternative 无法承载内嵌资源(如 CID 引用的图片),导致 iOS 客户端忽略或错误解析整个 HTML 体,最终呈现为空白或仅最后一张图。
✅ 正确结构:嵌套式 multipart(recommended)
必须采用两层边界嵌套:
- 外层:multipart/alternative(兼容纯文本客户端,提供降级内容)
- 内层(HTML 部分中):multipart/related(专为 HTML + 内联资源设计,确保 CID 关联生效)
以下是可直接落地的修正方案(已验证在 Apple Mail、Outlook iOS、Gmail App、Thunderbird 和桌面浏览器中一致显示):
$boundary = md5(uniqid(rand())); // 全局唯一边界标识
$outerBoundary = 'b1_' . $boundary;
$innerBoundary = 'b2_' . $boundary;
// ✅ 外层 headers:声明 multipart/alternative + 外边界
$headers = "MIME-Version: 1.0\r\n";
$headers .= "Content-Type: multipart/alternative; boundary=\"{$outerBoundary}\"\r\n";
$headers .= "From: \r\n";
$headers .= "Reply-To: no-reply@yourdomain.net\r\n";
// ✅ 构建完整邮件体(注意顺序与换行!)
$message = "";
// ▶ 第一部分:纯文本备选(Apple Mail 会忽略,但规范要求存在)
$message .= "--{$outerBoundary}\r\n";
$message .= "Content-Type: text/plain; charset=\"us-ascii\"\r\n";
$message .= "Content-Transfer-Encoding: 7bit\r\n\r\n";
$message .= "This email contains a visitor register with photos. Please view in HTML mode.\r\n\r\n";
// ▶ 第二部分:HTML 主体(类型为 multipart/related)
$message .= "--{$outerBoundary}\r\n";
$message .= "Content-Type: multipart/related; boundary=\"{$innerBoundary}\"; type=\"text/html\"\r\n\r\n";
// ▶ 内层 HTML 内容(注意:此处不写 --b2_xxx,由下一行开始)
$message .= "--{$innerBoundary}\r\n";
$message .= "Content-Type: text/html; charset=\"UTF-8\"\r\n";
$message .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
// 插入你的完整 HTML(含 @@##@@)
$message .= $mailHeader;
$message .= "Visitors on " . date("d/m/Y", strtotime($_POST['date'])) . "
";
$message .= $table;
$message .= $mailFooter;
$message .= "\r\n";
// ▶ 内层图片资源(每个图片必须在 --b2_xxx 边界内,且 Content-ID 严格匹配 HTML 中的 cid:xxx)
foreach ($images as $imgData) {
$imageName = $imgData['name']; // e.g., 'Robin0'
$imageBase64 = $imgData['base64']; // 原始 base64 字符串(不含 data:...)
$message .= "--{$innerBoundary}\r\n";
$message .= "Content-Type: image/jpeg; name=\"{$imageName}.jpg\"\r\n"; // ⚠️ 用 image/jpeg 而非 image/jpg(更标准)
$message .= "Content-Transfer-Encoding: base64\r\n";
$message .= "Content-ID: <{$imageName}>\r\n";
$message .= "Content-Disposition: inline; filename=\"{$imageName}.jpg\"\r\n\r\n";
$message .= chunk_split($imageBase64, 76, "\r\n"); // ✅ 每行 ≤76 字符,符合 RFC 2045
}
// ▶ 结束内层 multipart/related
$message .= "--{$innerBoundary}--\r\n";
// ▶ 结束外层 multipart/alternative
$message .= "--{$outerBoundary}--\r\n";
// 发送
mail($to, $subject, $message, $headers); ⚠️ 关键注意事项
- 边界命名不可重复:外层 b1_... 与内层 b2_... 必须不同,且全程统一大小写和引号(推荐双引号包裹 boundary 值);
- 换行符必须为 \r\n:Unix \n 在某些 SMTP 服务中会导致解析失败;
- Base64 数据需 chunk_split():RFC 强制要求每行 ≤76 字符,否则 Apple Mail 可能截断;
- Content-Type: image/jpeg 更可靠:image/jpg 并非标准 MIME 类型,部分 iOS 版本拒绝渲染;
- 不要省略 text/plain 部分:虽然 Apple Mail 不显示它,但缺失会导致部分邮件网关标记为异常;
- 避免在 HTML 中使用 data: URI:iOS Mail 不支持 base64 内联图片(src="data:image/..."),必须用 cid: + multipart/related。
✅ 验证建议
发送测试邮件后,在 Apple Mail 中:
- 右键 → “显示原始邮件”(或按 Cmd+Shift+U);
- 检查是否存在两层 boundary= 声明;
- 确认所有
都有对应 Content-ID:
且位于同一 multipart/related 边界内; - 查看 Base64 图片块是否以 \r\n\r\n 开头、以 \r\n 结尾,且无多余空行。
通过该结构,你将彻底规避 Apple Mail 的渲染缺陷,同时保持 Android、Web 客户端完美兼容——无需引入 PHPMailer 等依赖,纯原生 PHP 即可稳定交付专业级 HTML 邮件。










