dragover 不调用 preventDefault,drop 事件不会触发;必须在目标元素监听 dragover 并阻止默认行为,再从 dataTransfer.files 提取文件构造 FormData,用 fetch 上传时不可手动设置 Content-Type。

dragover 不调 preventDefault,drop 就不会触发
这是最常卡住的第一步:浏览器对拖入文件有默认行为(比如图片直接打开、文本显示内容、PDF 下载),drop 事件根本不会执行——除非你在 dragover 中明确阻止它。
必须在目标容器上监听 dragover,并在回调里写 event.preventDefault()。只监听 drop 而不处理 dragover,松手后页面跳转或白屏,dataTransfer.files 压根拿不到。
- 别绑在
document或body上,除非你真要全局接收;优先绑定到具体上传区域的 DOM 元素 - 如果用了 Vue/React,确保用
ref拿到真实 DOM 再调addEventListener,虚拟节点监听无效 - 调试时在
dragover回调加console.log('over'),没输出说明事件没绑定成功,或父级pointer-events: none拦截了
从 dataTransfer.files 取文件并构造 FormData
event.dataTransfer.files 是只读的 FileList,不是数组,不能直接用 map 或 forEach。上传前得先转成数组,再逐个校验和 append。
注意:FormData.append() 的第二个参数是 File 对象本身,不是路径也不是 base64 字符串——传错会导致后端收不到二进制流。
- 用
Array.from(event.dataTransfer.files)转换,避免[...files]在旧版 Safari 报错 - 每个
File对象有name、size、type属性,上传前务必检查:if (file.size > 20 * 1024 * 1024)限制 20MB - MIME 类型别硬比全等:
file.type === 'image/jpeg'会漏掉png和webp,改用file.type.startsWith('image/') - 多文件上传时,
FormData.append('files', file)可以复用同一个字段名,后端收到的是同名数组;如需区分,可用FormData.append('files[]', file)(取决于后端解析习惯)
用 fetch 上传时 Content-Type 必须留空
很多人手动设 Content-Type: multipart/form-data,结果后端收不到文件——这是大坑。fetch 发送 FormData 时,必须让浏览器自动生成 boundary,所以请求头里的 Content-Type 一定不能显式设置。
生成可在 deepbi 沙箱 iframe 内运行或作为独立文件运行的交互式 HTML 页面,使用 `window.deepbi` JS SDK 实现数据查询、空间管理、文件浏览及用户操作。当用户提出以下需求时启用该能力: - 创建 KPI 仪表盘、数据表格、文件浏览器、成员列表、管理页面; - 明确要求“转成 dhtml”、“转译成 HTML”、“把 XX 页面转成……”、“控制台页面”、“管理后台”、“DeepBI 页面迁移”; - 提及位于 `apps/console/src/featur
否则浏览器会按你写的类型发纯文本,multipart 结构被破坏,后端 req.files 或 $_FILES 为空。
- 正确写法:
fetch('/upload', { method: 'POST', body: formData })—— 不传headers选项 - 错误写法:
headers: { 'Content-Type': 'multipart/form-data' }—— 直接失效 - 如需带认证 token,加在 headers 里没问题,但避开
Content-Type即可:headers: { Authorization: 'Bearer xxx' } - 上传大文件建议加
signal配置 abortController,避免用户关页后请求还在跑
后端实际收到的是原始二进制流,不是 JSON
前端传的是 FormData,后端拿到的不是 { files: [...] } 这种 JSON,而是 HTTP multipart 请求体。Node.js(Express)需用 multer,PHP 用 $_FILES,Python(Flask)用 request.files——它们都依赖原生文件上传解析中间件。
如果你发现后端 req.body 是空对象、req.files 是 undefined,大概率是没配好解析器,或者前端误把 FormData 转成了 JSON 字符串再传。
- 切勿对
FormData调JSON.stringify(),它不可序列化 - 后端没启用 multipart 解析时,
console.log(req.headers['content-type'])通常能看到类似multipart/form-data; boundary=----WebKitFormBoundaryxxx,但 body 仍是 raw stream - 本地调试时,用 curl 模拟验证:
curl -F "files=@./test.jpg" http://localhost:3000/upload,结果一致才说明后端逻辑没问题
拖拽上传真正难的不是“怎么传”,而是边界情况:用户拖入 50 个文件、拖来 .exe、网络中断时如何提示、上传中刷新页面怎么办——这些都得靠 File 元数据校验、fetch 的 signal 控制、以及后端可靠的 multipart 解析共同兜底。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!










