webkitdirectory 属性仅改变文件选择器行为,需手动监听 change 或 drop 事件才能获取文件列表;event.target.files 仅含根目录一级文件,递归读取须用 webkitGetAsEntry() + createReader().readEntries(),且仅 Chromium 支持。

webkitdirectory 属性本身不触发回调,必须手动监听 change 或 drop 事件才能拿到文件列表——很多人卡在写了 input[type="file"][webkitdirectory] 却没写事件监听,以为浏览器会自动“调用你写的函数”。
为什么 webkitdirectory 选中文件夹后 event.target.files 是空的
常见错误是只加了属性但没处理事件流。Chrome/Edge 中,webkitdirectory 只影响文件选择器行为(允许选中整个文件夹),但 DOM 仍需显式监听 change 才能读取结果。
- 必须绑定
input.addEventListener('change', handler),不能只靠属性声明 -
event.target.files在文件夹场景下仍是FileList,但仅包含根目录下的一级文件(不含子目录内容) - 若想递归读取子目录,必须用
item.webkitGetAsEntry()+createReader().readEntries(),且仅 Chromium 支持 - Firefox 和 Safari 不支持
webkitdirectory,也不提供等效 API;Safari 对webkitGetAsEntry()的支持极有限(比如禁止访问上级路径)
拖拽文件夹时如何正确获取所有文件(含子目录)
点击选择和拖拽是两套机制:event.target.files 来自点击,e.dataTransfer.items 来自拖拽。后者才是递归读取文件夹的唯一可行入口。
- 必须阻止
dragover默认行为:e.preventDefault(),否则drop事件根本不会触发 - 遍历
e.dataTransfer.items,对每个item调用item.webkitGetAsEntry() - 拿到
Entry后,用isDirectory ? entry.createReader().readEntries(cb) : entry.file(cb)分支处理 - 注意:
readEntries()是异步且非递归的,需自己实现递归遍历逻辑(比如用队列或 Promise.all + map) - 不要试图把
DataTransferItemList直接转成数组用Array.from()—— 它不是类数组对象,得用for (let i = 0; i 遍历
FileReader 处理大文件夹时容易卡死怎么办
一次性预览或读取整个文件夹里所有文件的 content,主线程会阻塞。这不是 bug,是 FileReader 的设计限制:它只支持单个文件的异步读取,没有批量调度能力。
立即学习“前端免费学习笔记(深入)”;
- 别在
readEntries()回调里直接对每个file调reader.readAsDataURL(file)—— 这会导致大量并发读取,内存暴涨 - 改用节流策略:每次只处理 3–5 个文件,用
setTimeout或Promise.resolve().then()让出主线程 - 预览图片优先用
URL.createObjectURL(file),比readAsDataURL内存占用低一个数量级 - 若需校验文件内容(如哈希、JSON 格式),用
file.slice()提取前 1MB,再用readAsArrayBuffer读取,避免加载整文件 - 移动端尤其要注意:iOS Safari 对
webkitGetAsEntry()完全不支持,拖拽文件夹会退化为只拿到第一个文件
真正难的不是“怎么拿到文件”,而是判断哪些文件该进队列、哪些该跳过(比如 .DS_Store、Thumbs.db)、以及如何在用户还没松手时就给出反馈(比如显示“正在扫描 12 个子项…”)。这些细节不写进事件回调里,就永远不会有回调发生。



















