PHP原生不支持文件夹上传,需前端用webkitdirectory触发多文件选择并手动构造FormData,后端通过$_FILES二维数组逐个处理,同时严格校验路径防止遍历攻击。

PHP原生不支持文件夹上传,必须依赖前端配合
浏览器的 默认不提供文件夹选择能力,HTML5 虽引入了 webkitdirectory 和 directory 属性,但仅限 Chromium 系(Chrome、Edge)和 Firefox 部分版本,Safari 完全不支持。PHP 本身没有任何机制能“接收一个文件夹”,它只处理 HTTP POST 中的 $_FILES 数组——每个条目对应一个独立文件上传项。
前端需用 webkitdirectory 触发多文件递归读取
要让用户选中文件夹并上传其全部内容(含子目录),前端必须:
- 使用
- 监听
change事件,遍历event.target.files,注意:此时FileList是扁平化的,**不保留目录结构**;若需还原路径,必须用file.webkitRelativePath(仅 Chromium 支持) - 对每个
File构造FormData,手动添加路径前缀(如formData.append('files[]', file, file.webkitRelativePath || file.name)) -
后端 PHP 才能通过
$_FILES['files']['name']和$_FILES['files']['tmp_name']数组逐个处理
$_FILES 多文件上传时的数组结构容易误读
当用 multiple 上传 3 个文件时,$_FILES['files'] 不是普通数组,而是按字段维度组织的二维数组:
Array
(
[name] => Array
(
[0] => a.txt
[1] => sub/b.txt
[2] => c.jpg
)
[tmp_name] => Array
(
[0] => /tmp/phpabc123
[1] => /tmp/phpdef456
[2] => /tmp/phpghi789
)
)
正确遍历方式是:
部分功能简介:商品收藏夹功能热门商品最新商品分级价格功能自选风格打印结算页面内部短信箱商品评论增加上一商品,下一商品功能增强商家提示功能友情链接用户在线统计用户来访统计用户来访信息用户积分功能广告设置用户组分类邮件系统后台实现更新用户数据系统图片设置模板管理CSS风格管理申诉内容过滤功能用户注册过滤特征字符IP库管理及来访限制及管理压缩,恢复,备份数据库功能上传文件管理商品类别管理商品添加/修改/
立即学习“PHP免费学习笔记(深入)”;
for ($i = 0; $i < count($_FILES['files']['name']); $i++) {
$originalName = $_FILES['files']['name'][$i];
$tmpPath = $_FILES['files']['tmp_name'][$i];
// 注意:$originalName 可能含路径(如 'sub/b.txt'),需用 basename() 提取文件名
// 若需还原目录结构,应先解析 $originalName 得到相对路径,再拼到目标目录
}
服务端保存时路径处理不当会导致覆盖或越界
直接用 move_uploaded_file($tmpPath, '/var/www/uploads/' . basename($originalName)) 会丢失层级,所有文件都挤在根目录。更危险的是,若用户伪造 originalName 为 ../../etc/passwd,可能触发路径遍历。安全做法是:
- 用
pathinfo($originalName, PATHINFO_DIRNAME)解析相对路径,但**必须校验该路径不含..** - 用
str_replace(['\\', '..'], '', $relativeDir)或正则清除非法字符(更推荐realpath()+ 白名单根目录比对) - 目标存储路径应基于唯一上传 ID 构建,例如:
/var/www/uploads/20240520_abc123/sub/b.txt - 确保上传目录有写权限,且 Web 服务器无法直接执行其中的 PHP 文件(可配合
.htaccess或 Nginxlocation ~ \.php$ { deny all; })
真正难的不是上传动作本身,而是路径解析的健壮性、跨浏览器兼容性补丁、以及大文件夹下成百上千文件的并发写入稳定性——这些细节没压测过,上线就容易出问题。










