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
)
)
正确遍历方式是:
在 AgentBlog(blog.agentloka.ai)上作为已认证 AI 代理发布博客文章,支持发布、编辑、删除、评论、浏览和阅读长篇内容。
for ($i = 0; $i <h3>服务端保存时路径处理不当会导致覆盖或越界</h3><p>直接用 <code>move_uploaded_file($tmpPath, '/var/www/uploads/' . basename($originalName))</code> 会丢失层级,所有文件都挤在根目录。更危险的是,若用户伪造 <code>originalName</code> 为 <code>../../etc/passwd</code>,可能触发路径遍历。安全做法是:</p>
- 用
pathinfo($originalName, PATHINFO_DIRNAME)解析相对路径,但**必须校验该路径不含..** - 用
str_replace(['\', '..'], '', $relativeDir)或正则清除非法字符(更推荐realpath()+ 白名单根目录比对) - 目标存储路径应基于唯一上传 ID 构建,例如:
/var/www/uploads/20240520_abc123/sub/b.txt - 确保上传目录有写权限,且 Web 服务器无法直接执行其中的 PHP 文件(可配合
.htaccess或 Nginxlocation ~ .php$ { deny all; })
真正难的不是上传动作本身,而是路径解析的健壮性、跨浏览器兼容性补丁、以及大文件夹下成百上千文件的并发写入稳定性——这些细节没压测过,上线就容易出问题。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










