PHP 文件上传大小验证的正确实现方法

2次阅读

PHP 文件上传大小验证的正确实现方法

本文详解如何在 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 PHPMailerPHPMailerPHPMailer; use PHPMailerPHPMailerException; 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 检查可提升用户体验,但绝不可替代服务端验证——恶意用户可轻易绕过。

通过以上结构化校验,你将获得一个真正可靠的文件尺寸防线:超限文件在进入邮件发送流程前即被精准识别、友好提示并彻底阻断,同时兼顾安全性、可维护性与错误可观测性。

text=ZqhQzanResources