PHP原生不支持文件夹上传,需前端用webkitdirectory触发多文件选择并手动构造FormData,后端通过$_FILES二维数组逐个处理,同时严格校验路径防止遍历攻击。
PHP原生不支持文件夹上传,必须依赖
前端
配合
浏览器
的
默认不提供文件夹选择能力,HTML5 虽引入了
和
属性,但仅限 Chromium 系(Chrome、Edge)和 Firefox 部分版本,Safari 完全不支持。PHP 本身没有任何机制能“接收一个文件夹”,它只处理 HTTP POST 中的
数组——每个条目对应一个独立文件上传项。
前端需用
触发多文件递归读取
要让用户选中文件夹并上传其全部内容(含子目录),前端必须:
使用
监听
事件,遍历
,注意:此时
是扁平化的,**不保留目录结构**;若需还原路径,必须用
(仅 Chromium 支持)
对每个
构造
,手动添加路径前缀(如
)
后端 PHP 才能通过
和
数组逐个处理
多文件上传时的数组结构容易误读
当用
上传 3 个文件时,
不是普通数组,而是按字段维度组织的二维数组:
正确遍历方式是:
Comet AI 浏览器
下载 Comet AI 浏览器,体验由 Perplexity AI 驱动的革命性上网方式。内置 AI 助手可实时总结网页、跨标签页对比信息、自动执行任务。告别繁琐操作,让 AI 成为你的浏览副驾,大幅提升研究与工作效率。支持 Windows、macOS、Android 和 iOS。
下载
立即学习
“
PHP免费学习笔记(深入)
”;
服务端保存时路径处理不当会导致覆盖或越界
直接用
会丢失层级,所有文件都挤在根目录。更危险的是,若用户伪造
为
,可能触发路径遍历。安全做法是:
用
解析相对路径,但**必须校验该路径不含
**
用
或正则清除非法字符(更推荐
+ 白名单根目录比对)
目标存储路径应基于唯一上传 ID 构建,例如:
确保上传目录有写权限,且 Web 服务器无法直接执行其中的 PHP 文件(可配合
或 Nginx
)
真正难的不是上传动作本身,而是路径解析的健壮性、跨浏览器兼容性补丁、以及大文件夹下成百上千文件的并发写入稳定性——这些细节没压测过,上线就容易出问题。
webkitdirectorydirectory$_FILESwebkitdirectorychangeevent.target.filesFileListfile.webkitRelativePathFileFormDataformData.append('files[]', file, file.webkitRelativePath || file.name)$_FILES['files']['name']$_FILES['files']['tmp_name']$_FILESmultiple$_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
)
)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/passwdpathinfo($originalName, PATHINFO_DIRNAME)..str_replace(['\', '..'], '', $relativeDir)realpath()/var/www/uploads/20240520_abc123/sub/b.txt.htaccesslocation ~ .php$ { deny all; }