10.4FormData :前端文件上传与表单数据处理的核心工具
什么是 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;}// 创建 FormDataconst 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;}// 创建 FormDataconst 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 等中间件 |
