
6.3 FormData 数据处理
FormData 是浏览器原生提供的表单数据构造与处理接口,用于以键值对形式构建结构化表单数据,原生支持文件、二进制数据的封装与提交。
它是异步表单提交、文件上传、表单数据快速序列化的核心工具,能够直接对接 HTTP 请求的 multipart/form-data 编码格式,无需手动拼接数据与处理分隔边界。
6.3.1 核心操作方法
FormData 提供了完整的增删改查与遍历方法,支持动态构建表单数据,兼容字符串、文件、二进制等多种值类型。
一、两种创建方式
1. 创建空实例
手动构建所有字段,适合完全动态构造数据的场景。
javascript
const formData = new FormData();
2. 基于原生表单快速创建
传入表单 DOM 元素,自动收集所有带 name 属性的控件值,收集规则与原生表单提交完全一致:
- 仅收集带有 name 属性的控件
- disabled 禁用控件不会被收集
- 单选、复选框按选中状态收集
- 文件控件自动收集文件对象
javascript
const form = document.querySelector('#loginForm');
// 自动序列化整个表单为FormData
const formData = new FormData(form);
二、核心操作方法
|--------------------|------------|------------------------------|
| 方法 | 作用说明 | 核心特点 |
| append(key, value) | 追加键值对 | 同键名可多次追加,保留所有值;适合多文件上传、多选值提交 |
| set(key, value) | 设置键值对 | 同键名直接覆盖已有值;常规字段赋值推荐使用 |
| get(key) | 获取指定键的第一个值 | 返回单个值;绝大多数单值字段使用 |
| getAll(key) | 获取指定键的所有值 | 返回数组;用于多值字段、多文件上传场景 |
| delete(key) | 删除指定键及其所有值 | 移除整个字段 |
| has(key) | 判断是否存在指定键 | 返回布尔值,用于字段存在性判断 |
三、基础操作示例
javascript
const formData = new FormData();
// 设置普通字段
formData.set('username', 'zhangsan');
formData.set('age', 25);
// 追加多个同名字段(如多选爱好)
formData.append('hobby', 'reading');
formData.append('hobby', 'running');
// 获取值
console.log(formData.get('username')); // 'zhangsan'
console.log(formData.getAll('hobby')); // ['reading', 'running']
// 删除字段
formData.delete('age');
// 判断是否存在
console.log(formData.has('age')); // false
四、遍历方法
FormData 支持原生迭代器协议,可通过 entries()、keys()、values() 实现全量遍历:
javascript
// 遍历所有键值对
for (const [key, value] of formData.entries()) {
console.log(`${key}: ${value}`);
}
// 遍历所有键名
for (const key of formData.keys()) {
console.log(key);
}
五、文件与二进制支持
FormData 的核心优势是原生支持文件与二进制数据封装,值支持三种类型:字符串、File 对象、Blob 对象,提交时自动编码为 multipart/form-data 格式,是文件上传的标准方案。
javascript
const fileInput = document.querySelector('#avatar');
const file = fileInput.files[0];
// 添加文件字段,支持自定义文件名
formData.append('avatar', file, 'custom-filename.png');
// 添加Blob二进制数据
const blob = new Blob(['文本内容'], { type: 'text/plain' });
formData.append('doc', blob, 'readme.txt');
6.3.2 表单数据序列化与转换
实际业务中经常需要在 FormData、普通对象、查询字符串之间做格式转换,适配不同的请求场景与数据处理需求。
一、原生表单快速序列化
基于表单 DOM 直接生成 FormData 是最快的表单序列化方式,一行代码完成全表单数据收集。
javascript
function serializeForm(formSelector) {
const form = document.querySelector(formSelector);
return new FormData(form);
}
注意事项:
- 表单控件必须设置 name 属性,否则不会被收集
- disabled 状态的控件不会被序列化,需要提交的禁用字段请改用 readonly
- 文件控件的文件对象会被自动完整收集,无需额外处理
二、FormData 转普通对象
FormData 是迭代器结构,无法直接读取所有字段,通常转换为普通 JavaScript 对象进行数据处理与校验。
javascript
function formDataToObject(formData) {
const result = {};
for (const [key, value] of formData.entries()) {
// 已存在的键转为数组,处理多值字段
if (result.hasOwnProperty(key)) {
result[key] = Array.isArray(result[key])
? [...result[key], value]
: [result[key], value];
} else {
result[key] = value;
}
}
return result;
}
// 使用示例
const formData = new FormData(document.querySelector('#form'));
const data = formDataToObject(formData);
三、FormData 转 URL 查询字符串
GET 请求场景下,需要将表单数据转为 URL 参数拼接,可通过 URLSearchParams 快速转换。
javascript
const formData = new FormData(document.querySelector('#searchForm'));
// 转为 key=value&key2=value2 格式的查询字符串
const queryString = new URLSearchParams(formData).toString();
// 拼接请求
fetch(`/api/search?${queryString}`);
四、普通对象转 FormData
业务中构造好的 JS 对象,需要转为 FormData 进行提交时,遍历对象键值批量生成。
javascript
function objectToFormData(obj) {
const formData = new FormData();
Object.keys(obj).forEach(key => {
const value = obj[key];
// 数组类型循环追加
if (Array.isArray(value)) {
value.forEach(item => {
formData.append(key, item);
});
} else {
formData.set(key, value);
}
});
return formData;
}
// 使用示例
const user = {
username: 'lisi',
age: 30,
hobbies: ['music', 'sport']
};
const formData = objectToFormData(user);
嵌套对象处理:FormData 本身不支持嵌套结构,复杂嵌套对象通常有两种处理方式:
- 扁平化键名,如 user.name、user.age
- 转为 JSON 字符串作为单个字段提交,服务端解析
五、提交注意事项
使用 fetch 或 XMLHttpRequest 提交 FormData 时,不要手动设置 Content-Type 请求头。浏览器会自动生成带边界标识的 multipart/form-data 类型,手动设置会导致边界缺失,服务端无法正常解析数据。
javascript
// 正确提交示例
fetch('/api/submit', {
method: 'POST',
body: formData
// 无需设置Content-Type,浏览器自动处理
});