ThinkPHP多文件上传时_file验证规则不生效,因其默认只校验单文件字段,无法自动解析$_FILES中数组形式的多文件数据;需手动展开$_FILES为标准文件项列表并逐个验证。

ThinkPHP 多文件上传时 _file 验证规则为什么不生效
因为 _file 规则默认只作用于单个文件字段,不自动识别数组形式的多文件(如 files[]),验证器会把整个 $_FILES['files'] 当成一个“值”去校验,而不是遍历每个子项。
常见错误现象:maxSize 或 ext 校验完全没触发,或只报一次错但实际传了 5 个违规文件;$validate->batch(true) 也无效——这不是批量验证的问题,是根本没进到每个文件的校验流程。
- 必须手动提取
$_FILES['files']中的每个文件子数组(name、tmp_name等键需对齐) - 不能直接把
$_FILES['files']丢给Validate::check(),它不理解 PHP 文件上传数组的嵌套结构 - ThinkPHP 6.0+ 的
Validate类没有内置多文件展开逻辑,得自己 flatten
如何手动展开 $_FILES 数组并逐个验证
核心是把原始 $_FILES['files'] 转成形如 [0 => [...], 1 => [...], ...] 的标准文件项列表,再循环调用验证器。
使用场景:表单中用 <input type="file" name="files[]" multiple> 或多个独立 name="files[0]" 字段上传。
- 用
array_keys($_FILES['files']['name'])获取索引,再用foreach构建每个文件的独立$fileItem数组 - 每个
$fileItem必须包含name、tmp_name、size、type、error五个键,缺一不可,否则验证器判为非法文件 - 验证规则写法不变,比如
['size' => '1024*1024', 'ext' => 'jpg,png,gif'],但要应用在每个$fileItem上
$files = $_FILES['files'] ?? [];
$items = [];
foreach (array_keys($files['name']) as $i) {
$items[] = [
'name' => $files['name'][$i],
'tmp_name' => $files['tmp_name'][$i],
'size' => $files['size'][$i],
'type' => $files['type'][$i],
'error' => $files['error'][$i],
];
}
$rule = ['size' => '1024*1024', 'ext' => 'jpg,png'];
$validate = Validate::make($rule);
$errors = [];
foreach ($items as $item) {
if (!$validate->check($item)) {
$errors[] = $validate->getError();
}
}
validateFile 方法无法复用?别硬套内置文件验证逻辑
ThinkPHP 的 validateFile 是给单文件字段设计的,内部直接读 $_FILES[$field],不支持传入已展开的文件数组。强行传会导致 tmp_name 错位或 error 判定异常。
性能影响:每次循环新建 Validate 实例开销极小,比试图魔改 validateFile 更稳定;若文件数超 50,建议加 break 限制报错数量,避免前端刷屏。
- 不要写
$validate->validateFile($item, 'files')—— 第二个参数是字段名,不是数据 - 不要尝试给
$validate->rule(['files' => 'fileSize:1024*1024|fileExt:jpg'])然后check($_POST)—— 它不会自动拆$_FILES - 如果用了模型验证(
Model::validate(true)),同样得先 flatten 再传,模型不接管文件数组解析
上传前必做的两件事:error 值检查和空文件过滤
用户可能点了上传但没选文件,或浏览器上传中断,此时 $item['error'] === UPLOAD_ERR_NO_FILE,不处理就会被当成 0 字节文件继续走验证,浪费资源还报奇怪的 size 错误。
容易踩的坑:用 empty($item['tmp_name']) 判断文件是否存在——错!tmp_name 在 error !== 0 时也可能非空但无效。
- 必须优先检查
$item['error'] === UPLOAD_ERR_OK,其他UPLOAD_ERR_*常量直接跳过或报具体错误 $item['size'] === 0且error === UPLOAD_ERR_OK是合法空文件,是否允许由业务定,验证规则里size不会拦截它- 前端
multiple上传时,$_FILES['files']['name']可能是空数组,array_keys会返回空,需先isset($_FILES['files']['name']) && is_array(...)
$_FILES,这一步得自己来,而且必须严格按它的五个键构造。