拖拽超大文件卡顿的根源是浏览器在drop时同步解析元数据和缩略图,导致主线程阻塞;应禁用file.arrayBuffer()等同步I/O操作,仅用file.name/size/type等属性,改用FileReader分片读取并配合progress事件控制流程。

拖拽超大文件(如 >500MB)时卡顿,根本不是 dragover 事件本身慢,而是浏览器在拖拽过程中对 File 对象做元数据解析、缩略图预生成、甚至触发沙盒内同步 I/O,导致主线程被长时间阻塞。直接监听 drop 并调用 file.arrayBuffer() 就会卡死。
drop 事件里别碰 file.arrayBuffer() 或 file.text()
这是最常踩的坑:用户一松手,代码立刻执行 file.arrayBuffer(),浏览器被迫把整个文件读进内存再解析——哪怕只是想取个文件名或大小,也会触发底层全量扫描。
- 只用
file.name、file.size、file.type这些属性,它们是同步可读的,不触发 I/O - 避免任何同步读取方法:
file.text()、file.arrayBuffer()、file.slice(0, 1).arrayBuffer()都要禁用 - 如果真需要内容校验(如哈希),放到分片上传循环里按需读,用
file.slice(start, end).arrayBuffer()分段取
禁用 input[type="file"] 的多选预加载行为
用户一次拖入 10 个 2GB 文件,某些 WebView(尤其 iOS)会在你还没调任何方法前,就自动为每个 File 解析元数据+生成缩略图,造成 UI 假死。
- 用
event.dataTransfer.items替代event.dataTransfer.files,前者是惰性访问,后者某些浏览器会提前初始化 - 只取第一个文件:
event.dataTransfer.items[0]?.getAsFile(),跳过其余 - 若必须支持多文件,用
Array.from(event.dataTransfer.items).slice(0, 3)限流,避免批量触发
用 FileReader + progress 分片读取,而非一次性加载
即使只处理单个超大文件,也不能指望 file.arrayBuffer() 安全返回——它没有进度、不可中断、失败即重来。
立即学习“前端免费学习笔记(深入)”;
- 用
FileReader配合file.slice()分块读:reader.readAsArrayBuffer(file.slice(offset, offset + chunkSize)) - 每次
reader.onload后才切下一块,避免并发堆积;设置chunkSize = 4 * 1024 * 1024(4MB)较稳妥 - 监听
reader.onprogress更新 UI,但注意该事件在 Safari 中不触发,需 fallback 到计时器估算 - 加
reader.onerror捕获NotFoundError或SecurityError,这些在沙盒环境(如 Electron)中很常见
移动端拖拽文件需降级为点击选择
iOS 和 Android 大多数 WebView 根本不支持 dragover/drop 事件拖入本地文件,强行监听只会让页面无响应。
- 用
if ('ontouchstart' in window || navigator.maxTouchPoints > 0)检测移动端 - 直接隐藏拖拽区,显示
<input type="file" accept=".zip,.mp4">,并设capture="filesystem"(Android 支持) - 不要试图用
touchstart+touchmove模拟拖拽文件——文件系统权限不在 JS 控制范围内
真正卡住的从来不是“怎么传”,而是“什么时候开始读”。超大文件拖拽的边界不在前端 API,而在操作系统对 Web 沙盒的 I/O 限制。所有读取操作必须异步、分片、可取消,且默认不执行——直到用户明确点了“开始上传”按钮。



















