event.dataTransfer.items是唯一能可靠读取拖拽文件夹内容的入口,因files只暴露顶层项(如文件夹本身),不递归展开;items则提供可遍历的DataTransferItem列表,支持webkitGetAsEntry()递归访问子项,而Firefox仅支持getAsFile()扁平读取,iOS Safari和Android Chrome完全不支持文件夹拖拽。

event.dataTransfer.items 是唯一能可靠读取拖拽文件夹内容的入口,event.dataTransfer.files 对文件夹始终为空——这不是 bug,是浏览器规范行为。
为什么 files 里永远看不到文件夹里的文件
浏览器把“拖拽一个文件夹”和“拖拽一堆文件”当作两类操作:files 只暴露顶层被直接选中的项(即文件夹本身,类型为 application/x-directory),不递归展开;而 items 才包含可遍历的 DataTransferItem 列表,其中每个 item 可能是文件、目录,或剪贴板内容。
- Chrome / Edge 支持
item.webkitGetAsEntry(),返回FileSystemEntry(含isFile/isDirectory) - Firefox 不支持
webkitGetAsEntry,但可通过item.getAsFile()拿到单个文件(仅限扁平拖拽,不支持嵌套文件夹) - macOS 拖文件夹进网页时,
files里可能有 1 个伪文件(name 为文件夹名,size 为 0),别误判为有效文件
如何安全递归扫描文件夹(含兼容 fallback)
必须用 items + webkitGetAsEntry 启动递归,且需处理 Promise 链断裂、循环引用、权限拒绝等异常。
- 对每个
item调用item.webkitGetAsEntry(),判空后才继续 - 目录 entry 调用
createReader().readEntries(),该方法返回 Promise,不能用同步 for 循环遍历 - 遇到
DOMException: The request is not allowed by the user agent表示用户未授权访问文件系统,应降级提示“请改用文件选择器” - 递归深度超过 10 层时主动截断,避免栈溢出或卡死(尤其 Windows 网络驱动器下 symlink 可能成环)
示例关键逻辑:
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
立即学习“前端免费学习笔记(深入)”;
async function traverse(entry, path = '') {
if (entry.isFile) {
return new Promise(resolve => entry.file(file => resolve({ file, path: path + file.name })));
}
if (entry.isDirectory) {
const reader = entry.createReader();
const entries = await new Promise(resolve => reader.readEntries(resolve));
const results = [];
for (const e of entries) {
results.push(...await traverse(e, path + entry.name + '/'));
}
return results;
}
return [];
}
drop 之后必须立刻校验,否则上传会失败
用户拖一个 50GB 文件夹进来,前端不做任何拦截就发请求,后端大概率返回 413 Payload Too Large 或直接断连。校验不是可选项。
- 在
drop回调里遍历所有拿到的File对象,累加.size,超阈值(如 2GB)直接alert()或 UI 提示 - 检查单个文件名长度:Windows 路径总长限制 260 字符,过长文件名可能导致后端保存失败(尤其 Java 的
FileOutputStream) - 过滤掉
file.name为空或含控制字符(\x00-\x1f)的项,这些常来自错误挂载的 NAS 或损坏的 ZIP 解压 - 不要等分片上传时再校验——那时用户已等待十几秒,体验崩坏
移动端根本没戏,别白费劲
iOS Safari 和 Android Chrome 均不支持 dragover/drop 事件监听文件夹,items 为空,webkitGetAsEntry 未定义。这不是配置问题,是平台能力缺失。
- 检测到
'ontouchstart' in window或navigator.maxTouchPoints > 0时,直接隐藏拖拽区,显示<input type="file" webkitdirectory multiple> -
webkitdirectory在 iOS 上无效,所以移动端实际只能单文件上传;若业务强依赖文件夹,必须引导用户用桌面端 - 别尝试用
touchstart+preventDefault模拟拖拽——无法触发dataTransfer,纯属徒劳
真正难的从来不是写对那几行递归代码,而是当用户拖着整个“项目资料_2026_Q4”文件夹进来时,你得在 200ms 内判断它是否合法、是否过大、路径是否安全,并给出明确反馈——而不是让 loading 圈转到他切走页面。


















