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

PHP+Ajax带附件邮件发送失败问题排查与解决

带附件邮件发送失败的问题与解决方法

问题现象

无附件的邮件可以正常发送,但带附件的邮件无法发送,排查后怀疑是邮件Header配置问题,已耗费数小时仍未解决。

原代码问题分析

原代码存在几个关键错误:

  • Header被意外覆盖:构建带附件的Header时,第二行代码直接用=覆盖了之前定义的MIME-Version,导致邮件客户端无法识别多部分邮件格式
  • 邮件内容与Header混淆:把邮件正文、附件编码内容都塞进了$headers变量,而PHP的mail()函数要求邮件内容单独作为第三个参数传入,这种结构会完全打乱邮件格式
  • 固定边界符易冲突:使用固定字符串的MD5值作为边界符,可能和邮件内容中的字符重复,导致分界失效
  • 冗余嵌套结构:不必要的multipart/alternative嵌套增加了结构复杂度,容易引发格式错误

原代码

<?php
if($_POST)
{
    $to_email       = "xxxx@icloud.com";
    $from_email     = "noreply@xxx.pl";
     
    $subject    = "subject";
    $cnc        = $_POST["cnc_wheels"];
    $pain       = $_POST["new_paint_wheels"];
   
    $message_body = "email data";

    $file_attached = false;
    if(isset($_FILES["font_img_wheels"])) {
        $file_tmp_name    = $_FILES['font_img_wheels']['tmp_name'];
        $file_name        = $_FILES['font_img_wheels']['name'];
        $file_size        = $_FILES['font_img_wheels']['size'];
        $file_type        = $_FILES['font_img_wheels']['type'];
        $file_error       = $_FILES['font_img_wheels']['error'];

        if($file_error>0) {
            $mymsg = array( 
            1=>"上传的文件超过了php.ini中upload_max_filesize的限制", 
            2=>"上传的文件超过了HTML表单中指定的MAX_FILE_SIZE限制", 
            3=>"文件仅部分上传", 
            4=>"未上传任何文件", 
            6=>"缺少临时文件夹" ); 
            
            $output = json_encode(array('type'=>'error', 'text' => $mymsg[$file_error]));
            die($output); 
        }
        
        $handle = fopen($file_tmp_name, "r");
        $content = fread($handle, $file_size);
        fclose($handle);
        $encoded_content = chunk_split(base64_encode($content));
        $file_attached = true;
    }
    
    if($file_attached) {
         # 邮件头应适配多数客户端
         $headers = "MIME-Version: 1.0\r\n";
         $headers = "X-Mailer: PHP/" . phpversion()."\r\n";
         $headers .= "From: ".$from_email."\r\n";
         $headers .= "Subject: ".$subject."\r\n";
         $headers .= "Reply-To: ".$_POST["email_client"]."" . "\r\n";
         $headers .= "Content-Type: multipart/mixed; boundary=".md5('boundary1')."\r\n\r\n";
         
         $headers .= "--".md5('boundary1')."\r\n";
         $headers .= "Content-Type: multipart/alternative;  boundary=".md5('boundary2')."\r\n\r\n";
         
         $headers .= "--".md5('boundary2')."\r\n";
         $headers .= "Content-Type: text/plain; charset=utf-8\r\n\r\n";
         $headers .= $message_body."\r\n\r\n";
         
         $headers .= "--".md5('boundary2')."--\r\n";
         $headers .= "--".md5('boundary1')."\r\n";
         $headers .= "Content-Type:  ".$file_type."; ";
         $headers .= "name=\"" . $file_name . "\"\r\n";
         $headers .= "Content-Transfer-Encoding:base64\r\n";
         $headers .= "Content-Disposition:attachment; ";
         $headers .= "filename=\"" . $file_name . "\"\r\n";
         $headers .= "X-Attachment-Id:".rand(1000,9000)."\r\n\r\n";
         $headers .= $encoded_content."\r\n";
         $headers .= "--".md5('boundary1')."--";
    }else{
        $headers = 'From: '.$_POST["email_client"].'' . "\r\n" .
        'Reply-To: '.$_POST["email_client"].'' . "\r\n" .
        'X-Mailer: PHP/' . phpversion();
    }
   
    $send_mail = mail($to_email, $subject, $message_body, $headers);

    if(!$send_mail) {
        $output = json_encode(array('type'=>'error', 'text' => '邮件发送失败。'));
        die($output);
    }else {
        $output = json_encode(array('type'=>'message', 'text' => '感谢您的咨询,我们会尽快与您联系。'));
        die($output);
    }
}

修复后的代码

修复方案核心是正确分离Header和邮件内容,规范MIME多部分邮件结构,同时支持多附件上传:

<?php
if($_POST)
{
    $to_email       = "xxxx@icloud.com";
    $from_email     = "noreply@xxx.pl";
    $subject        = "subject";
    $message        = "email data";

    $boundary = md5(time());

    $headers = "From: $from_email\r\n";
    $headers .= "MIME-Version: 1.0\r\n";
    $headers .= "Content-Type: multipart/mixed; boundary=\"" . $boundary . "\"\r\n";

    $body = "--" . $boundary . "\r\n";
    $body .= "Content-Type: text/plain; charset=\"UTF-8\"\r\n";
    $body .= "Content-Transfer-Encoding: 8bit\r\n\r\n";
    $body .= $message . "\r\n";

    if ($_FILES["font_img_wheels"]["size"] > 0) {
        $attachment_name = $_FILES["font_img_wheels"]["name"];
        $attachment_tmp_name = $_FILES["font_img_wheels"]["tmp_name"];
        $attachment_data = file_get_contents($attachment_tmp_name);
        $attachment_encoded = chunk_split(base64_encode($attachment_data));

        $body .= "--" . $boundary . "\r\n";
        $body .= "Content-Type: application/octet-stream; name=\"" . $attachment_name . "\"\r\n";
        $body .= "Content-Transfer-Encoding: base64\r\n";
        $body .= "Content-Disposition: attachment\r\n\r\n";
        $body .= $attachment_encoded . "\r\n";
    }

    if ($_FILES["second_img_wheels"]["size"] > 0) {
        $attachment_name = $_FILES["second_img_wheels"]["name"];
        $attachment_tmp_name = $_FILES["second_img_wheels"]["tmp_name"];
        $attachment_data = file_get_contents($attachment_tmp_name);
        $attachment_encoded = chunk_split(base64_encode($attachment_data));

        $body .= "--" . $boundary . "\r\n";
        $body .= "Content-Type: application/octet-stream; name=\"" . $attachment_name . "\"\r\n";
        $body .= "Content-Transfer-Encoding: base64\r\n";
        $body .= "Content-Disposition: attachment\r\n\r\n";
        $body .= $attachment_encoded . "\r\n";
    }

    $body .= "--" . $boundary . "--";

    if(mail($to_email, $subject, $body, $headers)) {
        $output = json_encode(array('type'=>'message', 'text' => '感谢您的咨询,我们会尽快与您联系。'));
        die($output);
    }else {
        $output = json_encode(array('type'=>'error', 'text' => '邮件发送失败。'));
        die($output);
    }
}

修复说明

  1. 动态唯一边界符:用md5(time())生成随时间变化的边界符,避免和内容冲突
  2. 分离Header与邮件内容:Header只保留发件人、MIME版本、内容类型等元信息,邮件正文和附件都放在$body中
  3. 简化MIME结构:去掉冗余的嵌套结构,直接使用multipart/mixed处理带附件的邮件
  4. 简化文件读取:用file_get_contents()替代手动打开/读取文件的操作,代码更简洁
  5. 支持多附件:增加了第二个附件的处理逻辑,可扩展更多附件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 18:00:55