FormData

180 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,点击查看活动详情

这一章是关于发送 HTML 表单的:带有或不带文件,带有其他字段等。

FormData 对象可以提供帮助。你可能已经猜到了,它是表示 HTML 表单数据的对象。

构造函数是:

let formData = new FormData([form]);

如果提供了 HTML form 元素,它会自动捕获 form 元素字段。

FormData 的特殊之处在于网络方法(network methods),例如 fetch 可以接受一个 FormData 对象作为 body。它会被编码并发送出去,带有 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 请求并回应 “User saved”。

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');

// 列出 key/value 对
for(let [name, value] of formData) {
  alert(`${name} = ${value}`); // key1 = value1,然后是 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>