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

Laravel Mailable添加附件时Symfony MIME组件报TypeError

Laravel Mailable附件触发Symfony TypeError问题排查与解决

问题背景

开发Laravel应用时,自定义PdfEmail类发送带动态PDF和服务报告附件的邮件,本地运行正常,但部署到Hostinger时触发Symfony组件的TypeError:

TypeError: The body of "Symfony\Component\Mime\Part\TextPart" must be a string, a resource, or an instance of "Symfony\Component\Mime\Part\File" (got "null"). in /path/to/public_html/vendor/symfony/mime/Part/TextPart.php:52


1. 触发TypeError的核心原因

这个错误本质是Symfony无法找到你指定的附件文件,导致构建附件时传入了null作为文件内容,常见触发场景:

  • Hostinger环境存储权限不足:动态生成PDF时,mkdir($directory, 0755, true)创建的目录权限在Hostinger下可能不足以让PHP写入,导致$pdf->save($pdfPath)执行失败,文件根本没生成。
  • 存储磁盘路径不匹配:Attachment::fromStorage()默认使用Laravel配置的default存储磁盘,如果你的附件实际存在public磁盘(对应storage/app/public),但线上环境的default磁盘配置不是public,就会找不到文件。
  • 服务报告附件路径无效:部分$attachment->file对应的文件在Hostinger服务器上不存在(比如本地测试文件未同步到线上,或者路径为绝对路径导致线上无法识别)。

2. Attachment::fromStorage()与MIME设置的潜在问题

  • 磁盘指定缺失:你的代码中Attachment::fromStorage('service-reports/...')未明确指定磁盘,默认使用default磁盘。如果附件存在public磁盘,必须显式指定磁盘才能正确找到文件:
    Attachment::fromStorageDisk('public', 'service-reports/' . $this->serviceReport->id . '/service-report.pdf')
    
  • MIME设置的冗余性:动态PDF的MIME硬编码为application/pdf没问题,但如果getMimeType()因文件不存在返回null,即便日志打印MIME不为空,也无法掩盖文件本身找不到的核心问题。

3. 更优的Laravel Mailable附件处理方案

方案一:验证文件存在再添加附件

在添加每个附件前先检查文件是否存在,避免无效路径导致的全局错误:

use Illuminate\Support\Facades\Storage;

// 处理动态PDF附件
$pdfStoragePath = 'service-reports/' . $this->serviceReport->id . '/service-report.pdf';
if (Storage::disk('public')->exists($pdfStoragePath)) {
    $attachments[] = Attachment::fromStorageDisk('public', $pdfStoragePath)
        ->withMime('application/pdf');
}

// 处理服务报告关联附件
foreach ($this->serviceReport->attachments as $attachment) {
    $filePath = $attachment->file;
    if (Storage::disk('public')->exists($filePath)) {
        $attachments[] = Attachment::fromStorageDisk('public', $filePath)
            ->withMime($this->getMimeType($filePath));
    } else {
        \Log::warning("Missing attachment: {$filePath}");
    }
}

方案二:使用临时文件存储动态PDF

避免在持久化存储中生成临时PDF,用完自动删除,减少磁盘占用与清理成本:

use Illuminate\Http\File;

// 生成临时PDF文件
$tempPdf = tempnam(sys_get_temp_dir(), 'service-report');
$this->pdf->save($tempPdf);

// 添加临时PDF作为附件
$attachments[] = Attachment::fromPath($tempPdf)
    ->as('service-report.pdf')
    ->withMime('application/pdf');

// 邮件发送后自动删除临时文件
$this->afterSend(function () use ($tempPdf) {
    if (file_exists($tempPdf)) {
        unlink($tempPdf);
    }
});

方案三:统一处理磁盘与权限

  • 确保Hostinger服务器上storage/app/public目录的权限为755,且所属用户与PHP运行用户一致(Hostinger通常为www-data)。
  • 在config/filesystems.php中确认default磁盘配置,或始终在代码中显式指定磁盘,规避环境差异导致的路径问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 18:04:52