
本文详解如何在 php 中为多文件上传添加可靠的最大尺寸校验,修复常见逻辑错误(如错误的数组索引、忽略上传错误码、误判空文件等),确保在调用 phpmailer 发送邮件前彻底拦截超限附件。
本文详解如何在 php 中为多文件上传添加可靠的最大尺寸校验,修复常见逻辑错误(如错误的数组索引、忽略上传错误码、误判空文件等),确保在调用 phpmailer 发送邮件前彻底拦截超限附件。
在使用 PHP 处理多文件上传并集成 PHPMailer 发送带附件邮件时,文件大小验证失效是一个高频问题。典型表现是:代码看似包含 if ($_FILES['uploaded-file']['size'][$i] > $max_size) 判断,但超限文件仍被上传并附加到邮件中——根本原因往往不是逻辑缺失,而是对 $_FILES 超全局数组结构的误读与校验时机的错位。
✅ 正确理解 $_FILES 数组结构
PHP 的文件上传数据以二维数组形式组织在 $_FILES['field_name'] 下,每个子键(如 'name', 'size', 'tmp_name', 'error', 'type')本身已是独立的一维数组,对应每个上传文件。错误写法 $_FILES['uploaded-file']['name']['size'][$i] 试图在字符串(文件名)上取 'size' 键,必然失败且静默忽略(PHP 发出 Notice 但不中断执行)。
✅ 正确访问方式:
$fileSize = $_FILES['uploaded-file']['size'][$i]; // ✔️ 正确:size 是顶层键 $fileName = $_FILES['uploaded-file']['name'][$i]; // ✔️ 正确 $tmpPath = $_FILES['uploaded-file']['tmp_name'][$i]; // ✔️ 正确 $errorCode = $_FILES['uploaded-file']['error'][$i]; // ✔️ 必须检查!
✅ 完整、健壮的校验流程(含示例代码)
以下是一个生产就绪的校验片段,已整合进 PHPMailer 流程,并修复原文所有关键缺陷:
立即学习“PHP免费学习笔记(深入)”;
<?php
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\Exception;
require 'vendor/autoload.php';
// 配置参数
$maxFileSize = 2 * 1024 * 1024; // 2MB(注意:原文 1204 是笔误,应为 1024)
$uploadDir = 'uploads/';
// 创建邮件实例
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->Host = 'smtp.example.com';
$mail->SMTPAuth = true;
$mail->Username = 'your@email.com';
$mail->Password = 'your-app-password';
$mail->setFrom($_POST['email'] ?? '', $_POST['name'] ?? '');
$mail->addAddress('recipient@example.com');
// 构建邮件内容(略)
// ✅ 关键:严格校验文件上传状态与大小
$attachmentsValid = true;
$uploadedFiles = [];
if (isset($_FILES['uploaded-file']) && is_array($_FILES['uploaded-file']['name'])) {
$fileCount = count($_FILES['uploaded-file']['name']);
for ($i = 0; $i < $fileCount; $i++) {
$name = $_FILES['uploaded-file']['name'][$i];
$tmpName = $_FILES['uploaded-file']['tmp_name'][$i];
$size = $_FILES['uploaded-file']['size'][$i];
$error = $_FILES['uploaded-file']['error'][$i];
// ❌ 跳过无效上传(空文件、客户端取消、超限等)
if ($error !== UPLOAD_ERR_OK) {
$attachmentsValid = false;
error_log("File upload error #{$error} for '{$name}'");
continue;
}
// ❌ 跳过空文件(size === 0)
if ($size === 0) {
$attachmentsValid = false;
error_log("Empty file detected: '{$name}'");
continue;
}
// ✅ 核心:校验文件大小
if ($size > $maxFileSize) {
$attachmentsValid = false;
echo "<p style='color:red;'>Błąd: Plik '{$name}' przekracza dozwolony rozmiar (max. 2 MB).</p>";
continue;
}
// ✅ 安全生成唯一文件名,防止路径遍历 & 冲突
$safeName = basename($name);
$uniqueName = uniqid() . '_' . $safeName;
$targetPath = $uploadDir . $uniqueName;
// ✅ 移动临时文件(必须在 size 校验后!)
if (!move_uploaded_file($tmpName, $targetPath)) {
$attachmentsValid = false;
error_log("Failed to move uploaded file: {$name}");
continue;
}
$uploadedFiles[] = $targetPath;
}
}
// ❌ 任一文件校验失败,立即终止发送
if (!$attachmentsValid) {
echo "<p style='color:red;'>Wysyłka została anulowana z powodu błędów załączników.</p>";
exit;
}
// ✅ 所有附件校验通过,添加到邮件
foreach ($uploadedFiles as $filePath) {
if (file_exists($filePath)) {
$mail->addAttachment($filePath);
}
}
// 发送邮件
try {
$mail->send();
header('Location: sent.html');
exit;
} catch (Exception $e) {
error_log("Mailer error: " . $mail->ErrorInfo);
echo "Wystąpił błąd podczas wysyłki: " . $mail->ErrorInfo;
}⚠️ 关键注意事项与最佳实践
- 永远先检查 $_FILES['field']['error'][$i]:UPLOAD_ERR_OK(值为 0)是唯一安全继续的信号。其他错误(如 UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE)表明文件已被 PHP 层拒绝,此时 size 可能为 0 或不可信。
- isset($_FILES['field']) ≠ 有有效文件:该检查仅确认表单提交了该字段,但可能为空数组或含错误项。必须结合 is_array() 和循环内 error 码判断。
- 避免硬编码路径与不安全文件名:使用 basename() 过滤原始文件名,结合 uniqid() 生成唯一目标名,杜绝路径遍历(../../etc/passwd)和并发覆盖风险。
- 清理临时文件:若校验失败,已 move_uploaded_file 的文件需手动 unlink();本例中建议在 try/catch 外统一清理,或改用 tempnam() + 显式删除。
- 前端校验仅为辅助:HTML 的 accept 和 JS 的 file.size 检查可提升用户体验,但绝不可替代服务端验证——恶意用户可轻易绕过。
通过以上结构化校验,你将获得一个真正可靠的文件尺寸防线:超限文件在进入邮件发送流程前即被精准识别、友好提示并彻底阻断,同时兼顾安全性、可维护性与错误可观测性。











