拖拽上传时应直接读取 event.datatransfer.files,它是唯一可信来源;formdata.append 需用相同 key 多次调用才能让后端解析为数组;预览大量文件时须控制 filereader 并发读取以防内存溢出。

拖拽上传时 files 对象怎么拿才对
直接读 event.dataTransfer.files 就行,它和 input.files 结构完全一致,都是只读的 FileList。别去碰 event.target.files——那是 input 元素自己的值,跟拖拽无关。
常见错误是把拖拽和点击混用:比如监听了 drop 却又试图从某个隐藏的 input 里取 files,结果拿到的是旧数据或空值。
- 拖拽场景下,
event.dataTransfer.files是唯一可信来源 - 它不区分用户是点选还是拖入,也不受 iOS Safari 16.3 及更早版本对
multiple的静默降级影响(拖拽在这些版本中反而更稳定) - 如果要兼容取消拖拽操作,注意
event.dataTransfer.files.length === 0表示没拖进有效文件,不是错误
FormData.append 怎么写才能让后端收到数组
关键不是 append 多少次,而是 key 是否一致。后端能否解析成数组,取决于你是否用同一个字段名反复调用 append()。
例如:formData.append('files', file) 循环执行 5 次,Express 就能通过 req.files.files 拿到长度为 5 的数组;但如果写成 formData.append('files[' + i + ']', file),后端就得按索引字段分别接收,容易漏处理。
- PHP 默认覆盖同名字段,必须用
name="files[]"配合append('files[]', file) - Node.js(Express + multer)推荐统一用
append('files', file),再配multer.array('files') - 避免在循环里重复 new FormData(),应在循环外创建一次,再多次 append
为什么拖拽后预览卡顿甚至崩溃
一次性对几十个文件调用 FileReader.readAsDataURL() 是内存杀手。浏览器会尝试把所有文件内容转成 base64 字符串塞进内存,尤其图片类文件极易触发 OOM。
真正安全的做法是控制并发量、分批读取,或只读前 N 个用于展示。
- 用
let i = 0; const readNext = () => { if (i 手动节流 - 优先用
URL.createObjectURL(file)做缩略图,比 DataURL 快且省内存;用完记得revokeObjectURL - 超过 10 个文件时,跳过预览直接显示文件名+大小,避免用户等待
-
file.type不可靠(可能为空或伪造),校验应以二进制头信息为准,前端可略过,交由后端做
移动端拖拽支持到底靠不靠谱
Android 主流浏览器(Chrome、Edge、Firefox)支持良好,但微信安卓 WebView 和部分厂商定制系统仍会忽略 drop 事件,甚至不触发 dragover。
iOS Safari 从 16.4 开始才正式支持拖拽上传,16.3 及更早版本即使监听了事件也收不到 dataTransfer.files —— 它根本没实现这个 API。
- 必须 fallback 到传统
input[type="file"][multiple],不能只依赖拖拽 - 检测是否支持:尝试监听
dragover并阻止默认行为,再看是否触发;不触发就降级 - 移动端建议禁用
dragover的视觉反馈(比如高亮边框),因为触摸屏没有 hover 状态,反而干扰点击
实际落地时,最常被忽略的是后端接收逻辑和移动端降级路径——前者导致明明前端传了 10 个文件,后端只收到 1 个;后者让部分用户根本无法上传,却无任何提示。











