使用 FormData 提交带文件的复合表单时,需创建 FormData 实例并 append 文本字段与 File/Blob 对象,再直接传给 fetch 的 body 选项,且不可手动设置 Content-Type,否则会破坏浏览器自动生成的 multipart/form-data boundary。

使用 fetch 提交带文件的复合表单(如同时含文本字段和文件),核心是用 FormData 构造请求体,而不是手动拼 JSON 或设置 Content-Type —— 浏览器会自动设置正确的 multipart/form-data 边界(boundary)。
1. 创建 FormData 并追加字段和文件
把表单元素或手动构造的键值对添加进去。文件需为 File 或 Blob 对象(例如来自 <input type="file">):
const formData = new FormData();
// 添加普通文本字段
formData.append('title', '我的图片');
formData.append('description', '这是一张测试图');
// 添加文件(假设 input 元素已选中文件)
const fileInput = document.querySelector('#file-input');
if (fileInput.files.length > 0) {
const file = fileInput.files[0];
formData.append('image', file); // 字段名需与后端约定一致
}
// 也可追加多个同名文件(如多图上传)
// formData.append('images', file1);
// formData.append('images', file2);
2. 使用 fetch 发送请求(不设 headers)
关键点:不要手动设置 Content-Type。如果设置了(比如 'application/json' 或显式写 'multipart/form-data'),浏览器会屏蔽自动生成的 boundary,导致后端无法解析。
fetch('/api/upload', {
method: 'POST',
body: formData // 直接传入,无需 JSON.stringify
// ⚠️ 不要加 headers: { 'Content-Type': '...' }
})
.then(response => response.json())
.then(data => console.log('上传成功:', data))
.catch(err => console.error('上传失败:', err));
3. 后端接收注意点(简要提示)
服务端需支持 multipart/form-data 解析:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
- Node.js(Express):用
multer中间件处理文件上传 - Python(Flask):通过
request.files和request.form分别取文件和文本字段 - PHP:用
$_FILES和$_POST
4. 高级场景:添加进度监听(可选)
利用 XMLHttpRequest 的 upload.onprogress 是常见做法;fetch 原生不支持上传进度,但可通过 ReadableStream + TransformStream 拦截并计数(较复杂),多数项目仍倾向用 XMLHttpRequest 或封装好的库(如 axios)。
若坚持用 fetch,一种轻量方案是先读取文件为 ArrayBuffer,再手动分块上传(适合大文件断点续传),但这已超出“复合表单”范畴。

















