Javascript FormData

2023-02-17 10:57 更新

這一章是關(guān)于發(fā)送 HTML 表單的:帶有或不帶文件,帶有其他字段等。

FormData 對象可以提供幫助。你可能已經(jīng)猜到了,它是表示 HTML 表單數(shù)據(jù)的對象。

構(gòu)造函數(shù)是:

let formData = new FormData([form]);

如果提供了 HTML form 元素,它會自動捕獲 form 元素字段。

FormData 的特殊之處在于網(wǎng)絡(luò)方法(network methods),例如 fetch 可以接受一個 FormData 對象作為 body。它會被編碼并發(fā)送出去,帶有 Content-Type: multipart/form-data。

從服務(wù)器角度來看,它就像是一個普通的表單提交。

發(fā)送一個簡單的表單

我們先來發(fā)送一個簡單的表單。

正如你所看到的,它幾乎就是一行代碼:

<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>

在這個示例中,沒有將服務(wù)器代碼展示出來,因為它超出了我們當(dāng)前的學(xué)習(xí)范圍。服務(wù)器接受 POST 請求并回應(yīng) “User saved”。

FormData 方法

我們可以使用以下方法修改 ?FormData? 中的字段:

  • ?formData.append(name, value)? —— 添加具有給定 ?name? 和 ?value? 的表單字段,
  • ?formData.append(name, blob, fileName)? —— 添加一個字段,就像它是 ?<input type="file">?,第三個參數(shù) ?fileName? 設(shè)置文件名(而不是表單字段名),因為它是用戶文件系統(tǒng)中文件的名稱,
  • ?formData.delete(name)? —— 移除帶有給定 ?name? 的字段,
  • ?formData.get(name)? —— 獲取帶有給定 ?name? 的字段值,
  • ?formData.has(name)? —— 如果存在帶有給定 ?name? 的字段,則返回 ?true?,否則返回 ?false?。

從技術(shù)上來講,一個表單可以包含多個具有相同 name 的字段,因此,多次調(diào)用 append 將會添加多個具有相同名稱的字段。

還有一個 set 方法,語法與 append 相同。不同之處在于 .set 移除所有具有給定 name 的字段,然后附加一個新字段。因此,它確保了只有一個具有這種 name 的字段,其他的和 append 一樣:

  • ?formData.set(name, value)?,
  • ?formData.set(name, blob, fileName)?。

我們也可以使用 for..of 循環(huán)迭代 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
}

發(fā)送帶有文件的表單

表單始終以 Content-Type: multipart/form-data 來發(fā)送數(shù)據(jù),這個編碼允許發(fā)送文件。因此 <input type="file"> 字段也能被發(fā)送,類似于普通的表單提交。

這是具有這種形式的示例:

<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>

發(fā)送具有 Blob 數(shù)據(jù)的表單

正如我們在 Fetch 一章中所看到的,以 Blob 發(fā)送一個動態(tài)生成的二進制數(shù)據(jù),例如圖片,是很簡單的。我們可以直接將其作為 fetch 參數(shù)的 body。

但在實際中,通常更方便的發(fā)送圖片的方式不是單獨發(fā)送,而是將其作為表單的一部分,并帶有附加字段(例如 “name” 和其他 metadata)一起發(fā)送。

并且,服務(wù)器通常更適合接收多部分編碼的表單(multipart-encoded form),而不是原始的二進制數(shù)據(jù)。

下面這個例子使用 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"> 一樣,用戶從他們的文件系統(tǒng)中使用數(shù)據(jù) imageBlob(第二個參數(shù))提交了一個名為 image.png(第三個參數(shù))的文件。

服務(wù)器讀取表單數(shù)據(jù)和文件,就好像它是常規(guī)的表單提交一樣。

總結(jié)

FormData 對象用于捕獲 HTML 表單,并使用 fetch 或其他網(wǎng)絡(luò)方法提交。

我們可以從 HTML 表單創(chuàng)建 new FormData(form),也可以創(chuàng)建一個完全沒有表單的對象,然后使用以下方法附加字段:

  • ?formData.append(name, value)?
  • ?formData.append(name, blob, fileName)?
  • ?formData.set(name, value)?
  • ?formData.set(name, blob, fileName)?

讓我們在這里注意兩個特點:

  1. ?set? 方法會移除具有相同名稱(name)的字段,而 ?append? 不會。
  2. 要發(fā)送文件,需要使用三個參數(shù)的語法,最后一個參數(shù)是文件名,一般是通過 ?<input type="file">? 從用戶文件系統(tǒng)中獲取的。

其他方法是:

  • ?formData.delete(name)?
  • ?formData.get(name)?
  • ?formData.has(name)?

這就是它的全貌!


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號