要实现拖拽上传,必须监听 dragover(需 preventdefault 允许放置并可加高亮)和 drop(preventdefault 后读取 datatransfer.files);filelist 需转数组校验类型、后缀、总大小;用 formdata 逐个 append 文件上传;同时保留隐藏 file input 作为 ios/webview 降级方案。

直接用 input type="file" 无法触发拖拽上传,关键在于绕过它、接管用户拖入行为——核心是监听 dragover 和 drop,阻止浏览器默认动作,再从 dataTransfer.files 提取真实文件。
拖拽区域必须监听的两个事件
只绑 drop 是无效的。浏览器要求目标区域在拖入过程中明确“允许放置”,这个信号由 dragover 携带,且必须调用 e.preventDefault(),否则松手时 drop 根本不触发,甚至页面跳转到文件地址。
-
dragover:仅用于“许可放置”,必须加e.preventDefault();可顺便加高亮样式(如border: 2px dashed #007bff)提升反馈 -
drop:松手瞬间触发,同样要e.preventDefault(),然后读取e.dataTransfer.files
从 dataTransfer 正确提取并校验文件
e.dataTransfer.files 是只读的 FileList,不是数组,不能直接用 map 或 filter。需先转成数组再处理,并做基础拦截:
- 用
Array.from(files)转换,再检查数量(如if (files.length === 0) return) - 过滤非文件项:
.filter(f => f instanceof File),避免拖入网页链接或文件夹误触发 - 判断类型别只信
file.type,它可能为空或被篡改;更稳妥的是用后缀:file.name.split('.').pop().toLowerCase() === 'pdf' - 限制总大小(如 ≤100MB):
Array.from(files).reduce((sum, f) => sum + f.size, 0) > 100 * 1024 * 1024
用 FormData 构造请求并上传
不能把 FileList 直接传给 fetch,必须包装进 FormData。字段名要和后端约定一致(常见为 files 或 files[]):
- 创建表单:
const formData = new FormData() - 逐个追加:
Array.from(files).forEach(file => formData.append('files', file)) - 发起请求:
fetch('/api/upload', { method: 'POST', body: formData }) - 注意:
fetch自动设置Content-Type为multipart/form-data并带正确 boundary,无需手动设头
必须准备降级方案
iOS Safari 和多数安卓 WebView 完全禁用 dragover/drop,代码再对也无响应。这不是 bug,是平台限制。因此必须保留传统入口:
- 隐藏原生
<input type="file" id="file-input" multiple> - 用 CSS(如
position: absolute; opacity: 0;)覆盖在拖拽区上层 - 点击拖拽区时触发
file-input.click(),确保所有设备都有上传路径
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











