本章介绍如何发送 HTML 表单:带或不带文件,带附加字段等等。
FormData 对象可以帮助我们实现这一点。正如你可能猜到的,它是一个用于表示 HTML 表单数据的对象。
构造函数是
let formData = new FormData([form]);
如果提供了 HTML form
元素,它会自动捕获其字段。
FormData
的特别之处在于,网络方法(如 fetch
)可以接受 FormData
对象作为主体。它会被编码并以 Content-Type: multipart/form-data
的形式发送出去。
从服务器的角度来看,这看起来像一个普通的表单提交。
发送一个简单的表单
让我们先发送一个简单的表单。
如你所见,这几乎是一行代码。
<form id="formElem">
<input type="text" name="name" value="John">
<input type="text" name="surname" value="Smith">
<input type="submit">
</form>
<script>
formElem.onsubmit = async (e) => {
e.preventDefault();
let response = await fetch('/article/formdata/post/user', {
method: 'POST',
body: new FormData(formElem)
});
let result = await response.json();
alert(result.message);
};
</script>
在这个例子中,服务器代码没有展示,因为它超出了我们的范围。服务器接受 POST 请求并回复“用户已保存”。
FormData 方法
我们可以使用方法修改 FormData
中的字段
formData.append(name, value)
– 添加一个具有给定name
和value
的表单字段,formData.append(name, blob, fileName)
– 添加一个字段,就像它是<input type="file">
一样,第三个参数fileName
设置文件名(不是表单字段名),就像用户文件系统中的文件名一样,formData.delete(name)
– 删除具有给定name
的字段,formData.get(name)
– 获取具有给定name
的字段的值,formData.has(name)
– 如果存在具有给定name
的字段,则返回true
,否则返回false
从技术上讲,表单允许具有多个具有相同 name
的字段,因此对 append
的多次调用会添加更多同名字段。
还有一个 set
方法,语法与 append
相同。区别在于 .set
会删除所有具有给定 name
的字段,然后添加一个新字段。因此,它确保只有一个具有该 name
的字段,其余部分与 append
相同。
formData.set(name, value)
,formData.set(name, blob, fileName)
.
我们还可以使用 for..of
循环遍历 formData 字段
let formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');
// List key/value pairs
for(let [name, value] of formData) {
alert(`${name} = ${value}`); // key1 = value1, then key2 = value2
}
发送包含文件的表单
表单始终以 Content-Type: multipart/form-data
发送,这种编码允许发送文件。因此,<input type="file">
字段也会被发送,类似于普通的表单提交。
以下是一个包含此类表单的示例
<form id="formElem">
<input type="text" name="firstName" value="John">
Picture: <input type="file" name="picture" accept="image/*">
<input type="submit">
</form>
<script>
formElem.onsubmit = async (e) => {
e.preventDefault();
let response = await fetch('/article/formdata/post/user-avatar', {
method: 'POST',
body: new FormData(formElem)
});
let result = await response.json();
alert(result.message);
};
</script>
发送包含 Blob 数据的表单
正如我们在 Fetch 章节中所见,很容易发送动态生成的二进制数据,例如图像,作为 Blob
。我们可以直接将其作为 fetch
参数 body
提供。
然而,在实践中,通常将图像作为表单的一部分发送,而不是单独发送,并附带其他字段,例如“名称”和其他元数据,会更方便。
此外,服务器通常更适合接受多部分编码的表单,而不是原始二进制数据。
此示例使用 FormData
将来自 <canvas>
的图像以及其他一些字段作为表单提交。
<body style="margin:0">
<canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas>
<input type="button" value="Submit" onclick="submit()">
<script>
canvasElem.onmousemove = function(e) {
let ctx = canvasElem.getContext('2d');
ctx.lineTo(e.clientX, e.clientY);
ctx.stroke();
};
async function submit() {
let imageBlob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));
let formData = new FormData();
formData.append("firstName", "John");
formData.append("image", imageBlob, "image.png");
let response = await fetch('/article/formdata/post/image-form', {
method: 'POST',
body: formData
});
let result = await response.json();
alert(result.message);
}
</script>
</body>
请注意图像 Blob
是如何添加的。
formData.append("image", imageBlob, "image.png");
这与在表单中存在 <input type="file" name="image">
,并且访问者提交了一个名为 "image.png"
(第三个参数)的文件,该文件包含来自其文件系统的 imageBlob
(第二个参数)数据相同。
服务器读取表单数据和文件,就像它是一个常规的表单提交一样。
总结
FormData 对象用于捕获 HTML 表单并使用 fetch
或其他网络方法提交它。
我们可以从 HTML 表单创建 new FormData(form)
,也可以在没有表单的情况下创建对象,然后使用方法追加字段。
formData.append(name, value)
formData.append(name, blob, fileName)
formData.set(name, value)
formData.set(name, blob, fileName)
让我们注意这里有两个特殊之处。
set
方法会删除具有相同名称的字段,而append
不会。这是它们之间唯一的区别。- 要发送文件,需要使用 3 个参数的语法,最后一个参数是文件名,通常是从用户文件系统为
<input type="file">
获取的。
其他方法是
formData.delete(name)
formData.get(name)
formData.has(name)
就是这样!
评论
<code>
标签,对于多行代码,请将它们包装在<pre>
标签中,对于超过 10 行的代码,请使用沙箱(plnkr,jsbin,codepen…)。