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

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));

额外优化建议

  1. 关闭SSL验证会带来安全风险,建议保持默认开启(或设置为CURLOPT_SSL_VERIFYHOST=2、CURLOPT_SSL_VERIFYPEER=true)。
  2. 用Laravel内置的storage_path()生成文件路径,避免手动拼接出错。
  3. 优先使用config('services.mailgun.secret')而非自定义的config('app.mailgun_secret'),符合Laravel配置规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 07:32:22