Laravel项目使用Mailgun发送附件异常问题求助
问题:Laravel + Mailgun 发送附件显示为 something.bin
Laravel项目已通过Mailgun正常发送邮件,首次添加附件功能时出现异常:计划发送PDF、PNG/JPG等格式文件,但收到的邮件中附件显示为something.bin格式。相关代码与配置如下:
发送邮件类代码
class SendTicket extends Mailable { use Queueable, SerializesModels; function __construct($from, $to, $subject, $attachments) { if (str_contains($to, 'email.com')) { return; } $ch = curl_init(); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt($ch, CURLOPT_URL, 'https://api.eu.mailgun.net/v3/'.config('app.mailgun_domain').'/messages'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); // 附件路径,后续计划支持多附件 $path = public_path() . '../storage/app/' . $attachments[0]; $post = array( 'from' => $from, 'to' => $to, 'subject' => $subject, 'text' =>'teste', 'attachment' => $path, ); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); curl_setopt($ch, CURLOPT_USERPWD, 'api' . ':' . config('app.mailgun_secret')); $result = curl_exec($ch); if (curl_errno($ch)) { echo 'Error:' . curl_error($ch); } curl_close($ch); } }
config/services.php 配置
<?php return [ /* |-------------------------------------------------------------------------- | 第三方服务配置 |-------------------------------------------------------------------------- | | 本文件用于存储Mailgun、Postmark、AWS等第三方服务的凭证,为包提供统一的配置位置。 | */ 'mailgun' => [ 'domain' => env('MAILGUN_DOMAIN'), 'secret' => env('MAILGUN_SECRET'), 'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'), ], 'postmark' => [ 'token' => env('POSTMARK_TOKEN'), ], 'ses' => [ 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), ], ];
config/app.php 配置片段
'env' => env('APP_ENV', 'production'), 'mailgun_secret' => env('MAILGUN_SECRET', 'secret'), 'mailgun_domain' => env('MAILGUN_DOMAIN', 'domain'),
.env 配置
MAIL_MAILER=mailgun MAILGUN_ENDPOINT=api.eu.mailgun.net MAILGUN_DOMAIN=aaa.aaa.pt MAILGUN_SECRET=7----------------------------------------8c351df0 MAIL_FROM_ADDRESS=info@dream.com MAIL_FROM_NAME="${APP_NAME}"
问题原因与解决方案
核心问题
你虽然继承了Laravel的Mailable类,但并未使用Laravel自带的邮件构建逻辑,而是直接用curl调用Mailgun API。直接传递文件路径给attachment参数时,Mailgun无法识别文件的MIME类型和原始文件名,因此默认将附件标记为something.bin。
解决方案1:正确使用curl传递附件(适配多附件)
使用PHP的CURLFile类封装附件,明确指定文件名和MIME类型,让Mailgun正确识别:
class SendTicket extends Mailable { use Queueable, SerializesModels; function __construct($from, $to, $subject, $attachments) { if (str_contains($to, 'email.com')) { return; } $ch = curl_init(); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); // 建议开启SSL验证,提升安全性 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true); curl_setopt($ch, CURLOPT_URL, 'https://api.eu.mailgun.net/v3/'.config('services.mailgun.domain').'/messages'); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); $post = array( 'from' => $from, 'to' => $to, 'subject' => $subject, 'text' =>'teste', ); // 批量处理附件 foreach ($attachments as $index => $filePath) { $fullPath = storage_path('app/' . $filePath); // 用Laravel内置函数生成路径,避免拼接错误 if (file_exists($fullPath)) { $fileName = basename($fullPath); $mimeType = mime_content_type($fullPath); // 获取文件原生MIME类型 $post["attachment[$index]"] = new CURLFile($fullPath, $mimeType, $fileName); } } curl_setopt($ch, CURLOPT_POSTFIELDS, $post); curl_setopt($ch, CURLOPT_USERPWD, 'api' . ':' . config('services.mailgun.secret')); // 用官方配置项,符合规范 $result = curl_exec($ch); if (curl_errno($ch)) { echo 'Error:' . curl_error($ch); } curl_close($ch); } }
解决方案2:改用Laravel Mailable原生逻辑(更推荐)
既然已经继承了Mailable,直接用Laravel内置邮件系统更简洁,自动处理MIME类型和附件:
class SendTicket extends Mailable { use Queueable, SerializesModels; public $from; public $to; public $subject; public $attachments; function __construct($from, $to, $subject, $attachments) { $this->from = $from; $this->to = $to; $this->subject = $subject; $this->attachments = $attachments; } public function build() { if (str_contains($this->to, 'email.com')) { return; } $message = $this->from($this->from) ->to($this->to) ->subject($this->subject) ->text('teste'); // 可替换为视图文件,如view('emails.ticket') // 添加所有附件 foreach ($this->attachments as $filePath) { $fullPath = storage_path('app/' . $filePath); if (file_exists($fullPath)) { $message->attach($fullPath); } } return $message; } }
使用时通过Laravel的Mail门面发送:
Mail::send(new SendTicket($from, $to, $subject, $attachments));
额外优化建议
- 关闭SSL验证会带来安全风险,建议保持默认开启(或设置为
CURLOPT_SSL_VERIFYHOST=2、CURLOPT_SSL_VERIFYPEER=true)。 - 用Laravel内置的
storage_path()生成文件路径,避免手动拼接出错。 - 优先使用
config('services.mailgun.secret')而非自定义的config('app.mailgun_secret'),符合Laravel配置规范。
内容的提问来源于stack exchange,提问作者Erbi Silva
相关产品推荐
相关产品推荐

