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); } } ?>
核心修改点
- 修复电话号码过滤规则:将
FILTER_SANITIZE_EMAIL改为FILTER_SANITIZE_STRING,避免电话号码被错误处理 - 增加文件上传校验:
- 检查
$_FILES['userfile']是否存在 - 验证上传错误码为
UPLOAD_ERR_OK(上传成功) - 处理上传失败场景,返回明确错误信息
- 检查
- 优化附件显示:使用用户上传的原文件名作为附件名,提升体验
- 清理临时文件:注册 shutdown 函数,邮件发送后自动删除临时文件,避免服务器空间浪费
- 增强错误排查:邮件发送失败时返回PHPMailer的具体错误信息
额外注意事项
- 确保服务器临时目录(
sys_get_temp_dir())具备写入权限 - Gmail SMTP需开启"低安全性应用访问",或使用App密码(若开启两步验证)
- 可添加文件类型/大小限制(如仅允许pdf/doc/docx,大小不超过5MB),防范恶意上传
内容的提问来源于stack exchange,提问作者KingLouie
相关产品推荐
相关产品推荐

