原生HTML拖拽上传需手动处理dragover和drop事件,必须在dragover中调用e.preventDefault()阻止默认跳转,仅监听drop无效;FileList需转数组校验类型与大小;上传用FormData,预览优先URL.createObjectURL。

原生 HTML 拖拽上传框不需要任何库,但必须手动处理 dragover 和 drop 事件,否则拖进去就跳转或下载——这是最常卡住的一步。
为什么 drop 事件不触发?必须阻止 dragover 默认行为
浏览器对拖入文件有默认响应(比如打开图片、下载文本),drop 要生效,前提是 dragover 被显式拦截。只监听 drop 是无效的。
-
dragover回调里必须写e.preventDefault(),否则松手瞬间页面就跳转到文件 URI - 别在
document或body上全局监听——目标容器(如)要自己绑定- 如果用了 Vue/React,确保用
ref拿到真实 DOM 元素再addEventListener,虚拟节点绑不上如何安全获取并校验拖进来的文件
event.dataTransfer.files是只读的FileList,不是数组,不能直接用map或filter,得先转成数组处理。- 用
Array.from(e.dataTransfer.files)转换,再遍历校验file.type和file.size - 检查图片类型别写死
file.type === 'image/jpeg',改用file.type.startsWith('image/'),覆盖 png/webp/svg - 大小限制建议前端也做:比如
if (file.size > 20 * 1024 * 1024)提前提示,避免白传 - 注意
dataTransfer.items和dataTransfer.files的区别:前者含目录、链接等杂项,后者只含文件,上传用files更稳妥
怎么把拖进来的文件发给后端?FormData 是唯一正解
拖拽得到的是
File对象,不是的 value,没法模拟表单提交。必须用FormData手动构造请求体。- 创建
new FormData(),然后formData.append('files', file)——键名按后端约定来,比如单文件用'file',多文件统一用'files' - 不要手动设
Content-Type头,fetch或XMLHttpRequest会自动生成带boundary的multipart/form-data - 后端收不到文件?大概率被中间层拦了:
Nginx需调大client_max_body_size,express + multer要配limits: { fileSize: 20 * 1024 * 1024 },Flask得改MAX_CONTENT_LENGTH
预览图片别用 FileReader 读 base64,优先用 URL.createObjectURL
FileReader.readAsDataURL会把整个文件读进内存再转 base64,大图容易卡顿甚至 OOM;URL.createObjectURL(file)只生成一个本地引用地址,轻量又实时。- 预览时直接赋值给
,上传成功后再URL.revokeObjectURL(url)释放引用 - 非图片文件(如 PDF、.txt)不建议预览,
file.type和file.name必须双重校验,防用户拖 .exe 或超大日志 - 如果需要裁剪或压缩,再上 Canvas;纯预览场景,
createObjectURL就够了
真正麻烦的从来不是拖拽本身,而是 dragover 不阻止导致的跳转、后端配置遗漏造成的静默失败、以及 FileList 类型判断太窄漏掉合法格式——这些点没踩过两三次,很难写出健壮的上传框。
- 如果用了 Vue/React,确保用
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











