什么是 FormData?
FormData 是一个 JavaScript 对象,用于表示 HTML 表单数据。它可以模拟表单控件的键值对,并支持 字符串 和 二进制数据(如文件) 的混合提交。
✅ 主要用途:
- 文件上传(图片、视频、文档等)
- 提交包含文件的复杂表单
- 构造
multipart/form-data请求体 - 与
fetch、XMLHttpRequest、axios配合使用
创建 FormData 实例
1. 空的 FormData
const formData = new FormData();
2. 从现有 <form> 元素创建
<form id="myForm">
<input name="username" value="Alice" />
<input type="file" name="avatar" />
</form>
const form = document.getElementById('myForm');
const formData = new FormData(form);
// 自动包含所有表单字段
三、添加数据:append(key, value, filename?)
语法:
formData.append(key, value, filename);
| 参数 | 说明 |
|---|---|
key |
字段名(字符串) |
value |
值,可以是字符串、Blob、File |
filename |
可选,上传时的文件名(仅当 value 是 Blob/File 时有效) |
示例:
const formData = new FormData();
// 添加字符串
formData.append('username', 'Alice');
formData.append('email', 'alice@example.com');
// 添加文件(来自 input)
const fileInput = document.querySelector('input[type=file]');
const file = fileInput.files[0];
formData.append('avatar', file, 'my-avatar.jpg'); // 指定文件名
// 添加 Blob(如 canvas 截图)
canvas.toBlob(blob => {
formData.append('screenshot', blob, 'screen.png');
});
// 添加多文件(同名)
formData.append('photos', file1);
formData.append('photos', file2);
读取数据的方法
由于 FormData 不是普通对象,不能用 formData.key 取值。
1. formData.get(key) ------ 获取第一个匹配的值
适用于单值字段(如 name、email 等)
const formData = new FormData();
formData.append('username', 'Alice');
formData.append('avatar', file); // file 是 File 对象
console.log(formData.get('username')); // "Alice"
console.log(formData.get('avatar')); // File 对象
⚠️ 注意:如果同一个 key 有多个值,get() 只返回第一个。
2. formData.getAll(key) ------ 获取某个 key 的所有值
适用于多文件上传或复选框等场景
formData.append('photos', file1);
formData.append('photos', file2);
console.log(formData.getAll('photos'));
// [File, File]
3. formData.has(key) ------ 判断是否包含某个 key
if (formData.has('username')) {
console.log('包含用户名');
}
4. 遍历所有数据:使用 formData.entries()(推荐)
for (let [key, value] of formData.entries()) {
console.log(key, value);
// 示例输出:
// username "Alice"
// avatar File { name: "avatar.jpg", ... }
}
// 遍历所有键值对
for (let [key, value] of formData.entries()) {
console.log(key, value);
}
// 只遍历键
for (let key of formData.keys()) {
console.log(key);
}
// 只遍历值
for (let value of formData.values()) {
console.log(value);
}
✅ 这是最常用的方式,适合调试或序列化所有数据。
5. 转成普通对象(仅限字符串值,不推荐用于文件)
function formDataToObject(formData) {
const obj = {};
for (let [key, value] of formData.entries()) {
// 注意:如果是 File 对象,这里会变成 [object File]
obj[key] = value;
}
return obj;
}
const obj = formDataToObject(formData);
console.log(obj); // { username: "Alice", avatar: File }
⚠️ 注意:如果值是 File 或 Blob,转成对象后无法还原为原始文件内容,仅用于查看结构。
修改与删除
1. set(key, value, filename?) ------ 设置值(会覆盖已有)
formData.set('username', 'Bob'); // 替换所有 username 的值
⚠️ 注意:
set()会删除所有同名字段,再添加新值。
2. delete(key) ------ 删除某个字段的所有值
formData.delete('tempImage');
发送请求(与 fetch / axios 配合)
✅ 使用 fetch
//原生 fetch 不支持上传进度事件(不像 axios 的 onUploadProgress)
<input type="text" id="username" value="Alice" />
<input type="file" id="avatar" />
<button onclick="upload()">上传</button>
async function upload() {
const username = document.getElementById('username').value;
const fileInput = document.getElementById('avatar');
const file = fileInput.files[0];
if (!file) {
alert('请先选择文件');
return;
}
// 创建 FormData
const formData = new FormData();
formData.append('username', username);
formData.append('avatar', file); // 字段名需与后端一致
try {
const response = await fetch('/api/upload', {
method: 'POST',
body: formData
// ⚠️ 不要设置 Content-Type!
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
console.log('上传成功:', result);
} catch (error) {
console.error('上传失败:', error);
}
}
✅ 使用 axios
<input type="file" id="avatar" />
<input type="text" id="username" value="Alice" />
<button onclick="upload()">上传</button>
async function upload() {
const fileInput = document.getElementById('avatar');
const usernameInput = document.getElementById('username');
const file = fileInput.files[0];
const username = usernameInput.value;
if (!file) {
alert('请选择文件');
return;
}
// 创建 FormData
const formData = new FormData();
formData.append('username', username);
formData.append('avatar', file); // 字段名需与后端一致
try {
const response = await axios.post('/api/upload', formData, {
// ⚠️ 注意:不要设置 Content-Type
// headers: {
// 'Content-Type': 'multipart/form-data' // ❌ 错误!会破坏 boundary
// },
onUploadProgress: (progressEvent) => {
const percent = (progressEvent.loaded / progressEvent.total) * 100;
console.log(`上传进度: ${percent.toFixed(2)}%`);
}
});
console.log('上传成功:', response.data);
} catch (error) {
console.error('上传失败:', error.response?.data || error.message);
}
}
🔔 重要 :不要手动设置
Content-Type
📂 七、文件处理技巧
1. 从网络图片创建 File 并添加
async function addImageFromURL(url) {
const response = await fetch(url);
const blob = await response.blob();
const file = new File([blob], 'online.jpg', { type: blob.type });
formData.append('image', file);
}

2. 限制文件类型/大小
const file = fileInput.files[0];
if (file.size > 2 * 1024 * 1024) {
alert('文件不能超过 2MB');
return;
}
if (!['image/jpeg', 'image/png'].includes(file.type)) {
alert('只支持 JPG/PNG');
return;
}
formData.append('photo', file);
常见注意事项
| 问题 | 解决方案 |
|---|---|
手动设置 Content-Type |
❌ 不要设置,让浏览器自动设置 |
无法用 formData.key 取值 |
✅ 使用 get() / getAll() |
FormData 不是响应式(Vue) |
✅ 用普通数据管理,最后生成 FormData |
| 跨域请求携带 cookie | ✅ fetch 加 credentials: 'include',axios 加 withCredentials: true |
| 后端收不到文件 | ✅ 检查字段名是否匹配,是否用了 multer 等中间件 |