附件名称和大小看着正常,下载后PDF阅读器却提示文件损坏。不要先换SMTP服务;先把生成、组装MIME、传输三个环节拆开,找到文件在哪一步改变。

第一步:保存发送前的原始字节

$pdfBytes = $pdf->render();
file_put_contents(BP . '/var/tmp/invoice-test.pdf', $pdfBytes);
// 仅在测试环境,完成后删除测试文件

用 file var/tmp/invoice-test.pdf 和 pdfinfo 检查。原始文件打不开,问题在PDF生成或输出缓冲;能打开则继续检查邮件组装。不要把真实客户发票留在可公开访问的pub目录。

最常见的是重复编码

现代Laminas/Mime Part接收原始二进制,并根据encoding生成Base64。如果代码先 base64_encode($pdfBytes),又设置 ENCODING_BASE64,收件人会得到二次编码的文本。确保只编码一次。

$part = new \Laminas\Mime\Part($pdfBytes);
$part->type = 'application/pdf';
$part->disposition = \Laminas\Mime\Mime::DISPOSITION_ATTACHMENT;
$part->encoding = \Laminas\Mime\Mime::ENCODING_BASE64;
$part->filename = 'invoice.pdf';

抓一份.eml对比

测试环境把最终MimeMessage写入var/tmp/test.eml,检查Content-Type、Content-Transfer-Encoding、boundary和文件名。把附件段Base64解码后计算哈希,与原始PDF比较:

sha256sum var/tmp/invoice-test.pdf
# 从eml提取附件后
sha256sum var/tmp/invoice-from-eml.pdf

还有两类隐蔽污染

  • PHP warning或BOM被写进PDF字节:查看文件头是否以%PDF开头;
  • 邮件库升级后仍调用旧的setBodyHtml/setParts组合,导致正文与附件互相覆盖。对照当前Magento版本的TransportBuilder实现,不要照搬旧博客代码。

最后用两个不同邮件服务商和Webmail/客户端各测一次。若.eml中的哈希已正确但只有某一SMTP通道损坏,再检查网关的内容过滤和大小限制。