浏览器无法直接上传文件夹,需用户主动选择;可用选单个文件夹(Chrome/Edge/Safari支持,Firefox不支持),或showDirectoryPicker()(仅Chromium、HTTPS)、或拖拽+webkitGetAsEntry()(多文件夹唯一方案);所有方式均需后端解析webkitRelativePath或手动构造路径来还原目录结构,且须防范路径穿越。

不能直接“上传文件夹”——浏览器不提供 fs.readdir 那样的目录遍历能力,所有文件访问必须由用户主动触发选择,且仅限于被选中的路径及其子项。所谓“上传文件夹”,本质是让用户选中一个目录,前端拿到其下所有 File 对象,再逐个提交。
用 选中单个文件夹
这是最直接、兼容性最好的方式(Chrome/Edge/新版 Safari 支持,Firefox 不支持)。关键点是同时写 webkitdirectory 和 directory 属性,兼顾历史与标准草案:
-
webkitdirectory是实际起效的属性,没有它,directory在当前主流浏览器里无效 -
multiple在此场景下无意义——它只影响多文件选择,对目录选择无效;选一次只能选一个文件夹 - 选中后,
e.target.files返回的是扁平化的FileList,不保留原始层级结构;但每个File的webkitRelativePath属性会带路径信息(如"docs/report.pdf"),可用于还原目录树 - 注意:该 API 只在 HTTPS 或
localhost下可用,file://协议直接报错或静默失败
用 showDirectoryPicker() 获取可遍历的目录句柄
这是更现代、能力更强的方案,但限制更严:仅 Chromium 系列(Chrome/Edge ≥95)、必须 HTTPS、需用户显式点击触发(不能自动调用)。
- 返回的是
FileSystemDirectoryHandle,可递归调用handle.values()遍历子项,区分FileSystemFileHandle和FileSystemDirectoryHandle - 能真实反映嵌套结构,比
webkitRelativePath更可靠(后者在某些系统上为空) - 不能直接上传;需用
fileHandle.getFile()读取每个文件为File实例,再塞进FormData - 错误常见于:未在用户手势(如
click)中调用 → 报DOMException: Permission denied;或页面非 HTTPS →SecurityError
拖拽多个文件夹:靠 dataTransfer.items + webkitGetAsEntry()
无法多选文件夹,但拖拽可以。这是目前唯一能一次选多个文件夹的可行路径,依赖 WebKit/Blink 内核的私有方法:
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
- 监听
drop事件,遍历e.dataTransfer.items - 对每个
item调用item.webkitGetAsEntry(),再判断entry.isDirectory - 对目录 entry 调用
entry.createReader().readEntries()递归读取(注意:该 API 异步且无深度限制,需自己控制递归防止栈溢出) - 所有读出的
File对象统一收集,用file.webkitRelativePath或手动拼接路径来保留结构 - Firefox 完全不支持
webkitGetAsEntry,此时只能退化为只接收拖入的文件(忽略文件夹)
上传时怎么保留文件夹结构?关键看后端怎么解析 webkitRelativePath
前端把文件塞进 FormData 时,append('files', file, file.webkitRelativePath || file.name) 这一行决定后端能否还原路径:
- 如果
file.webkitRelativePath为空(如 Firefox 或某些系统),后端收不到路径信息,只能存成平铺文件名,结构丢失 - 后端收到的字段值是字符串路径(如
"src/index.js"),需按/拆分并创建对应目录,再写入文件;不能直接用该路径做磁盘写入,要防路径穿越(如"../etc/passwd") - Node.js 后端常用
path.join(uploadRoot, safePath)+path.relative()校验;Python Flask 建议用os.path.normpath()并检查是否仍位于根目录内 - 若用
showDirectoryPicker(),前端可自行拼接路径(如dirHandle.name + '/' + fileHandle.name),但要注意编码和非法字符处理
真正难的不是“怎么选”,而是“选完之后怎么让后端理解你选的是什么结构”。webkitRelativePath 看似简单,但它在不同浏览器、不同操作系统下的表现差异极大,且无法被 polyfill。生产环境务必做好降级:当路径不可用时,至少保证文件内容不丢,结构信息作为可选元数据处理。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










