使用 Fetch 提交 multipart/form-data 时,应直接传入 FormData 实例作为 body,不手动设置 Content-Type 头,浏览器会自动添加带 boundary 的正确类型并组织请求体;手动设置会导致解析失败。

用 Fetch 提交 multipart/form-data 数据,核心是让浏览器自动生成边界(boundary)和正确格式的请求体——这只需传入一个 FormData 实例给 fetch() 的 body 选项,**不要手动设置 Content-Type 头**。
直接传 FormData,让浏览器自动处理
浏览器在检测到 body 是 FormData 对象时,会自动:
- 设置
Content-Type为multipart/form-data; boundary=----WebKitFormBoundary... - 按规范组织字段、文件、分隔符和换行
- 正确编码二进制内容(如图片 Blob)
✅ 正确写法:
const formData = new FormData();
formData.append('username', 'Alice');
formData.append('avatar', fileInput.files[0]); // File 对象
fetch('/upload', {
method: 'POST',
body: formData // ✅ 不要加 headers: { 'Content-Type': '...'' }
});
常见错误:手动设置 Content-Type
如果显式设置了 Content-Type: multipart/form-data,浏览器就不再自动添加 boundary,导致后端无法解析:
- ❌ 错误:请求体变成纯文本,无 boundary,无换行,文件内容被当字符串发送
- ❌ 错误:后端收到空字段或解析失败(如 Express 的
req.body为空,req.files未触发)
⚠️ 只有在你手动生成完整 multipart 字符串(极少见)时才需手动设头,日常上传绝不用。
需要额外 header?只加非 Content-Type 的
如需认证、自定义标识等,可安全添加其他头:
fetch('/upload', {
method: 'POST',
body: formData,
headers: {
'Authorization': 'Bearer abc123',
'X-Request-ID': 'req-789'
// ❌ 不要在这里写 'Content-Type'
}
});
浏览器仍会覆盖并设置正确的 Content-Type,其他头不受影响。
调试技巧:看 Network 面板里的真实请求
在浏览器开发者工具中检查请求:
- 看 Headers → Request Headers 中
Content-Type是否带boundary= - 看 Payload → Request Payload 是否显示为“form data”,展开能看到字段和文件名
- 若显示为“Request Payload”且内容是纯 JSON 或乱码,说明没传
FormData或手动设了错误的Content-Type
不复杂但容易忽略。

















