必须在 dragover 中调用 event.preventDefault() 并设 dropEffect='copy',drop 中同样需 preventDefault();FileList 需转数组过滤 image/ 类型;预览优先用 URL.createObjectURL 并及时 revoke;尺寸校验需异步加载 Image 获取 naturalWidth/Height。
浏览器默认会阻止 dragover 和 drop 的默认行为,不加处理的话,图片拖进来直接打开新页面或下载,根本不会触发上传逻辑。
必须在 dragover 中调用 event.preventDefault(),同时建议设置 event.dataTransfer.dropEffect = 'copy' 明确提示用户这是“复制上传”操作;drop 里同样要 preventDefault(),否则事件可能被中断。
drop 不够——没 dragover 阻止默认行为,拖入时浏览器就接管了dragenter 可选,用于高亮区域,但不是必需;dragleave 用来取消高亮,注意它会在子元素间频繁触发,建议用计时器防抖body 或大容器上直接监听——容易误触,推荐绑定到明确的 <div id="drop-area"> 这类语义化区域event.dataTransfer.files 是 FileList,不是数组,不能直接用 map 或展开运算符;图片可能混在其他文件(如 .txt、.pdf)里,需要手动过滤。
关键判断依据是 file.type.startsWith('image/'),而不是只看后缀名——后缀可伪造,MIME 类型由浏览器解析文件头得出,更可靠。
立即学习“前端免费学习笔记(深入)”;
Array.from(event.dataTransfer.files) 转成数组再 filter,避免原生 FileList 方法缺失问题files[0] 即可,但务必先判空:if (!files.length) return
FileReader 预览,或统一用 FormData.append('images', file) 批量提交FileReader 的 readAsDataURL 会把图片转成 base64 字符串,大图(比如 5MB 手机照片)可能导致内存飙升、UI 卡顿,甚至 iOS Safari 崩溃。
更稳妥的方式是用 URL.createObjectURL(file) 创建临时对象 URL,它不经过 JS 内存,直接指向浏览器内部缓存,性能好、兼容性也好(IE10+ 支持)。
createObjectURL 后必须在不用时调用 URL.revokeObjectURL(url),否则内存泄漏——尤其在反复拖拽预览时FileReader 和 createObjectURL,如需兼容,得降级为传统表单 + <input type="file">
<img> 的 src 设为对象 URL 后,加载失败不会抛 JS 错误,要用 img.onerror 捕获并清理 URL前端校验只是用户体验优化,不能替代服务端检查。但用户拖错图(比如拖了个 10MB 视频)再等十几秒上传完才报错,体验极差,所以基础校验有必要。
尺寸校验必须解码图片——File 对象本身不包含宽高信息,得靠 Image 对象加载后读取 naturalWidth/naturalHeight;这个过程是异步的,不能和上传逻辑串行阻塞。
file.type,辅以文件头检测:读取 file.slice(0, 4) 的 ArrayBuffer,比对 JPEG(ffd8ff)、PNG(89504e47)等 magic bytesfile.name 后缀判断类型——用户可以轻易把 evil.exe 改成 evil.jpg
真正麻烦的是并发控制和错误重试:用户连续拖三张图,每张都走 FileReader → 尺寸校验 → 上传,中间任意一步失败都要有明确反馈,且不能影响其他图片。这部分逻辑很容易写散,建议封装成 Promise 链或用 AbortController 统一管理请求生命周期。