FormData
是一个用于表单数据的键值对,可以通过FormData
对象来模拟表单提交,也可以通过FormData
对象来实现文件上传。
1. 简单认识
FormData
提供了一种表示表单数据的键值对构造方式,可以轻松的将数据通过XMLHttpRequest.send()
方法发送到服务器,如果送出时的编码类型被设为 multipart/form-data
,它会使用和表单一样的格式。
最常见的使用场景就是文件上传,但是FormData
还可以使用到其他的场景,比如模拟表单提交。
2. 使用
2.1 构造函数
FormData
是一个构造函数,它可以接受一个可选的参数,但是在现代浏览器中,它只接受一个HTMLFormElement
对象,如果传入其他类型的参数,会抛出一个TypeError
错误。
const formData = new FormData();
or
<form id="form">
<input type="text" name="name" value="张三">
<input type="text" name="age" value="18">
</form>
<script>
const form = document.getElementById('form');
const formData = new FormData(form);
</script>
注意:表单项的
name
属性是必须的,否则FormData
对象中不会包含该表单项。
2.2 方法
方法 | 描述 |
---|---|
append() | 添加一个键值对到FormData 对象中 |
delete() | 删除一个键值对 |
get() | 获取一个键值对的值 |
getAll() | 获取一个键值对的所有值 |
has() | 判断FormData 对象中是否包含某个键值对 |
set() | 设置一个键值对的值 |
keys() | 返回一个包含所有键名的迭代器 |
values() | 返回一个包含所有键值的迭代器 |
entries() | 返回一个包含所有键值对的迭代器 |
FormData
可以直接使用for...of
循环来遍历,也可以使用forEach()
方法来遍历。
const formData = new FormData();
formData.append('name', '张三');
formData.append('age', 18);
for (const [key, value] of formData) {
console.log(key, value);
}
formData.forEach((value, key) => {
console.log(key, value);
});
FormData
对象中的键值对是有序的,也就是说,键值对的顺序和添加的顺序是一致的。
同时,FormData
对象中的键名是不允许重复的,如果重复添加(append
)同一个键名,那么会存在两个同名的键值对。
FormData
不可以直接打印,如果直接打印,会打印出一个空对象,如果想要打印出键值对,可以使用for...of
循环或者forEach()
方法,打印单个值可以使用get
方法。
FormData
其实还是很简单的,上面的提供的方法也很简单,直接就见名知意了,所以就不过多的介绍了,下面就来看看它的使用场景。
3. 使用场景
使用场景最常用的就是文件上传了,下面就来看看如何使用FormData
来实现文件上传。
3.1 文件上传
<form id="form">
<input type="file" name="file">
<button type="submit">提交</button>
</form>
<script>
const form = document.getElementById('form');
form.addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(form);
const xhr = new XMLHttpRequest();
xhr.setRequestHeader('Content-Type', 'multipart/form-data');
xhr.open('POST', '/upload');
xhr.send(formData);
});
</script>
上面的代码中,我们使用FormData
来构造一个formData
对象,然后使用XMLHttpRequest
对象来发送请求,这样就可以实现文件上传了。
3.2 表单数据序列化
<form id="form">
<input type="text" name="name" value="张三">
<input type="text" name="age" value="18">
<button type="submit">提交</button>
</form>
<script>
const form = document.getElementById('form');
form.addEventListener('submit', (e) => {
e.preventDefault();
const formData = new FormData(form);
const params = new URLSearchParams(formData);
console.log(params.toString());
});
</script>
以前我们使用jQuery
来实现表单数据序列化的时候,需要使用serialize()
方法,但是现在我们可以直接使用FormData
对象来实现表单数据序列化了。
URLSearchParams
是URL
的一个属性,可以用来序列化URL
的查询字符串,也可以用来序列化FormData
对象,使用UrlSearchParams.toString()
可以将URLSearchParams
对象序列化为一个查询字符串,这里不一定非要用FormData
。
3.3 模拟表单提交
const formData = new FormData();
formData.append('name', '张三');
formData.append('age', 18);
const xhr = new XMLHttpRequest();
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.open('POST', '/submit');
xhr.send(formData);
模拟表单提交一般都是浏览器默认完成的,但是有时候我们需要自己来实现,这时候就可以使用FormData
对象来实现了。
4. 文件分片上传
开始上大招了,上面的就是为了凑字数的,下面我们来看看如何使用FormData
来实现文件分片上传。
4.1 文件分片上传的原理
文件分片上传的原理就是将一个文件分成多个小文件,然后分别上传,这样就可以实现大文件的上传了。
4.2 文件分片上传的实现
const file = document.getElementById('file').files[0];
const chunkSize = 1024 * 1024 * 2; // 2M
const chunkCount = Math.ceil(file.size / chunkSize);
for (let i = 0; i < chunkCount; i++) {
const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);
xhrFileUpload(chunk, i);
}
function xhrFileUpload(chunk, index) {
const formData = new FormData();
formData.append('file', chunk);
formData.append('index', index);
const xhr = new XMLHttpRequest();
xhr.setRequestHeader('Content-Type', 'multipart/form-data');
xhr.open('POST', '/upload');
xhr.send(formData);
}
上面的代码中,我们使用file.slice()
方法来将文件分片,然后使用FormData
对象来构造一个formData
对象,最后使用XMLHttpRequest
对象来发送请求,这样就可以实现文件分片上传了。
当然上面还有一个问题,就是文件分片上传的时候,这里是分为2M
一个分片,如果文件太大,那么分片的数量就会很多,这样就会导致请求的数量很多,这样就会导致服务器压力很大,这里是一个优化空间,提示可以使用Web Worker
来实现。
5. 总结
FormData
对象是一个非常强大的对象,它可以用来构造表单数据,也可以用来模拟表单提交,还可以用来实现文件分片上传,这里只是简单的介绍了一下,更多的内容可以参考MDN。