fetch 上传文件必须用 FormData 包装,因 File 作为 Blob 直传会以 application/octet-stream 发送,缺失 multipart/form-data 的字段名与 boundary,导致后端(如 Express+multer、Django)无法识别而 400;字段名须严格匹配后端约定(如 'avatar'、'document'),禁用点或中括号。

fetch 上传文件必须配合 FormData,直接传 File 对象会失败——浏览器报 TypeError: Request with GET/HEAD method cannot have body 或后端收不到字段,根本原因是没走 multipart 流程。
为什么不能直接 fetch(file)?
因为 File 是 Blob 子类,但 fetch 的 body 接收它时,默认按 application/octet-stream 发送,没有 multipart/form-data 的字段包装和 boundary 分隔。后端(如 Express + multer、Django)只认 multipart 格式里的命名字段(如 file),裸 Blob 会被忽略或 400。
常见错误现象:
- 控制台无报错,但响应状态是 400,后端日志显示 “no file in request”
- 用
curl -F "file=@xxx.jpg" ...能通,但前端fetch不行 - 误设了
Content-Type: multipart/form-data,导致 boundary 缺失,后端解析崩溃
FormData.append() 的字段名怎么写?
字段名(第一个参数)不是随便起的,它必须和后端约定一致。比如:
立即学习“前端免费学习笔记(深入)”;
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
- Express + multer 配了
upload.single('avatar')→ 前端必须formData.append('avatar', file) - Django 视图里写
request.FILES.get('document')→ 前端得用'document' - 字段名含点或中括号(如
'user.profile.avatar')多数后端不支持,别这么写
如果要传多个文件,用 multiple 属性后,遍历 input.files 即可:
for (let i = 0; i < input.files.length; i++) {
formData.append('files', input.files[i]); // 同名多值,后端收数组
}
fetch 配置里哪些项绝对不能动?
headers 里只要不碰 Content-Type 就安全;其他自定义头(如 Authorization、token)照常加。重点:
- ✅ 删掉或注释掉
'Content-Type': 'multipart/form-data'—— 浏览器必须自动设,手动设=破坏 boundary - ✅
method: 'POST'必须显式写,否则 fetch 默认 GET,GET 请求带 body 直接抛错 - ✅ 检查响应:
if (!res.ok) throw new Error(await res.text()),因为 fetch 对 4xx/5xx 不 reject - ❌ 不要用
JSON.stringify()包File,base64 体积膨胀 33%,且后端还得解码,纯属增加负担
大文件或需要进度条怎么办?
原生 fetch **不支持上传进度事件**。如果你看到网上“fetch + onprogress”的代码,基本是混淆了 XMLHttpRequest 的 API。真要进度条,只有两个选择:
- 换
XMLHttpRequest:它有upload.onprogress,兼容性好(IE11+) - 用
ReadableStream手动分片(高级玩法):读取file.stream(),切块、并发上传、拼接校验,但需后端配合,复杂度陡增
小文件(fetch + FormData 最稳;超过 20MB 就该考虑分片,而不是硬扛——用户关页面、网络抖动、Nginx 默认 client_max_body_size 1m 都会默默失败。


















