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

PHPMailer表单上传文件无法作为Gmail TLS邮件附件求助

问题分析与修复方案

你的文件无法附加到邮件的核心问题是前端未正确传递文件数据,后端也缺少必要的文件上传校验逻辑,以下是分步修复方案:

一、前端JS修复

当前JS代码仅传递了文件名(字符串),而非实际的文件对象,导致后端接收不到上传的文件。修改如下:

修改后的JS代码

document.addEventListener("DOMContentLoaded", function(event) {
    var contactForm = document.getElementById('frmContact');
    var templateUrl = '<?= get_bloginfo("template_url"); ?>/blocks/sendmessage_recruit.php';
    if (contactForm) {
        contactForm.addEventListener("submit", function(e) {
            e.preventDefault();
            var mailStatusEl = document.getElementById('mail-status');
            var sendMessageEl = document.getElementById('send-message');
            var loaderIconEl = document.getElementById('loader-icon');
            mailStatusEl.style.display = "none";
            sendMessageEl.style.display = "none";
            loaderIconEl.style.display = "block";
            
            // 获取表单字段值
            var name = document.querySelector('input[name="name"]').value;
            var email = document.querySelector('input[name="email"]').value;
            var phone = document.querySelector('input[name="phone"]').value;
            var content = document.querySelector('textarea[name="content"]').value;
            // 获取文件对象(而非文件名)
            var userfileInput = document.querySelector('input[name="userfile"]');
            var userfile = userfileInput.files[0];

            var xhttp = new XMLHttpRequest();
            xhttp.onreadystatechange = function() {
                mailStatusEl.style.display = "block";
                if (this.readyState == 4) {
                    if (this.status == 200) {
                        loaderIconEl.style.display = "none";
                        var responseData = JSON.parse(this.responseText);
                        if (responseData.type == 'error') {
                            sendMessageEl.style.display = "block";
                            mailStatusEl.className += " error";
                        } else {
                            sendMessageEl.style.display = "none";
                            mailStatusEl.className += " success";
                        }
                        mailStatusEl.innerHTML= responseData.text;
                    } else {
                        sendMessageEl.style.display = "block";
                        mailStatusEl.className += " error";                        
                        mailStatusEl.innerHTML = "Oh no! Something went wrong! Get in touch with us at info@northwindhealthcare.com";
                    }
                }
            }
            
            // 构建FormData,直接添加字段和文件
            var formData = new FormData();
            formData.append("name", name);
            formData.append("email", email);
            formData.append("phone", phone);
            formData.append("content", content);
            // 仅当用户选择文件时添加
            if (userfile) {
                formData.append("userfile", userfile);
            }
            
            xhttp.open("POST", templateUrl, true);
            // 无需手动设置Content-Type,浏览器会自动处理
            xhttp.send(formData);
        });
    }  
});

核心修改点

  • 替换userfile的获取方式:从.value(文件名)改为files[0](实际文件对象)
  • 直接向FormData附加文件对象,而非字符串
  • 移除冗余的data对象,简化数据传递逻辑

二、后端PHP修复

后端需要补充文件上传校验,同时修正几个逻辑错误:

修改后的后端代码

use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;

require 'PHPMailer/src/Exception.php';
require 'PHPMailer/src/PHPMailer.php';
require 'PHPMailer/src/SMTP.php';
require 'PHPMailer/src/OAuth.php';

// 仅处理表单提交请求
if (array_key_exists('email', $_POST)) {
    date_default_timezone_set('Etc/UTC');

    // 初始化PHPMailer实例
    $mail = new PHPMailer(true);

    // SMTP配置
    $mail->isSMTP();
    $mail->SMTPAuth   = true; 
    $mail->SMTPSecure = 'tls';
    $mail->Host = 'smtp.gmail.com';
    $mail->Port = '587';
    $mail->isHTML(false);
    $mail->Username = '#########';
    $mail->Password = '########';

    // 过滤表单输入
    $user_name      = filter_var($_POST["name"], FILTER_SANITIZE_STRING);
    $user_email     = filter_var($_POST["email"], FILTER_SANITIZE_EMAIL);
    // 修复:电话号码不能用邮箱过滤规则
    $user_phone     = filter_var($_POST["phone"], FILTER_SANITIZE_STRING);
    $content        = filter_var($_POST["content"], FILTER_SANITIZE_STRING);

    $empty = [];
    if(empty($user_name)) {
        $empty[] = "<b>Name</b>";       
    }
    if(empty($user_email)) {
        $empty[] = "<b>Email</b>";
    }
    if(empty($user_phone)) {
        $empty[] = "<b>Contact number</b>";
    }  
    if(empty($content)) {
        $empty[] = "<b>Message</b>";
    }
    if(!empty($empty)) {
        $output = json_encode(array('type'=>'error', 'text' => implode(", ",$empty) . ' Required!'));
        die($output);
    }

    // 设置邮件收发地址
    $mail->setFrom('#######', '######');
    $mail->addAddress('#######', '######');
   
    // 处理文件上传
    if (isset($_FILES['userfile']) && $_FILES['userfile']['error'] == UPLOAD_ERR_OK) {
        $uploadfile = tempnam(sys_get_temp_dir(), hash('sha256', $_FILES['userfile']['name']));
        if (move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile)) {
            // 使用原文件名作为附件显示名
            $mail->addAttachment($uploadfile, $_FILES['userfile']['name']);
            // 邮件发送后自动删除临时文件
            register_shutdown_function(function() use ($uploadfile) {
                unlink($uploadfile);
            });
        } else {
            $output = json_encode(array('type'=>'error', 'text' => 'Failed to save uploaded file.'));
            die($output);
        }
    } elseif (isset($_FILES['userfile']) && $_FILES['userfile']['error'] != UPLOAD_ERR_NO_FILE) {
        // 处理文件上传失败的情况
        $output = json_encode(array('type'=>'error', 'text' => 'File upload error: ' . $_FILES['userfile']['error']));
        die($output);
    }

    // 邮箱格式验证
    if(!filter_var($user_email, FILTER_VALIDATE_EMAIL)){
        $output = json_encode(array('type'=>'error', 'text' => '<b>'.$user_email.'</b> is an invalid Email, please correct it.'));
        die($output);
    }

    // 设置回复地址与邮件内容
    if ($mail->addReplyTo($_POST['email'], $_POST['name'])) {
        $mail->Subject = '##### | RECRUIT enquiry from ' . $user_name;
        $mail->Body = <<<EOT
Email: {$_POST['email']}
Contact Number: {$_POST['phone']}
Name: {$_POST['name']}
Message: {$_POST['content']}
EOT;

        // 发送邮件并处理结果
        if (!$mail->send()) {
            $output = json_encode(array('type'=>'error', 'text' => 'Unable to send email: ' . $mail->ErrorInfo));
            die($output);
        } else {
            $output = json_encode(array('type'=>'message', 'text' => 'Hi '.$user_name .', thank you for the message. We will get back to you shortly.'));
            die($output);
        }
    } else {
        $output = json_encode(array('type'=>'error', 'text' => 'Invalid email, please contact'.SENDER_EMAIL));
        die($output);
    }
}
?>

核心修改点

  1. 修复电话号码过滤规则:将FILTER_SANITIZE_EMAIL改为FILTER_SANITIZE_STRING,避免电话号码被错误处理
  2. 增加文件上传校验:
    • 检查$_FILES['userfile']是否存在
    • 验证上传错误码为UPLOAD_ERR_OK(上传成功)
    • 处理上传失败场景,返回明确错误信息
  3. 优化附件显示:使用用户上传的原文件名作为附件名,提升体验
  4. 清理临时文件:注册 shutdown 函数,邮件发送后自动删除临时文件,避免服务器空间浪费
  5. 增强错误排查:邮件发送失败时返回PHPMailer的具体错误信息

额外注意事项

  • 确保服务器临时目录(sys_get_temp_dir())具备写入权限
  • Gmail SMTP需开启"低安全性应用访问",或使用App密码(若开启两步验证)
  • 可添加文件类型/大小限制(如仅允许pdf/doc/docx,大小不超过5MB),防范恶意上传

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 03:25:54