Объект formData

Объекты FormData позволяют вам легко конструировать наборы пар ключ-значение, представляющие поля формы и их значения, которые в дальнейшем можно отправить с помощью Fetch или XMLHttpRequest

FormData использует такой же формат на выходе, как если бы мы отправляли обыкновенную форму с encoding установленным в "multipart/form-data".

Конструктор:

let formData = new FormData([form]);

Если передать в конструктор элемент HTML-формы form, то создаваемый объект автоматически прочитает из неё поля.

Его особенность заключается в том, что методы для работы с сетью, например fetch, позволяют указать объект FormData в свойстве тела запроса body.

Он будет соответствующим образом закодирован и отправлен с заголовком Content-Type: form/multipart.

То есть, для сервера это выглядит как обычная отправка формы.

Отправка простой формы

Давайте сначала отправим простую форму.

Как вы видите, код очень компактный:

<form id="formElem">
  <input type="text" name="name" value="John">
  <input type="text" name="surname" value="Smith">
  <input type="submit">
</form>
<p id="msg"></pm> 

<script>
  formElem.onsubmit = async (e) => {
    e.preventDefault();

    let response = await fetch('xhr/f1.php?n=1', {
      method: 'POST',
      body: new FormData(formElem)
    });

    document.getElementById("msg").innerHTML = await response.text();
  };
</script>

В этом примере серверный код не представлен, он принимает POST-запрос с данными формы и отвечает сообщением «Пользователь сохранён».

Методы объекта FormData

С помощью указанных ниже методов мы можем изменять поля в объекте FormData:

Технически форма может иметь много полей с одним и тем же именем name, поэтому несколько вызовов append добавят несколько полей с одинаковыми именами.

Ещё существует метод set, его синтаксис такой же, как у append. Разница в том, что .set удаляет все уже имеющиеся поля с именем name и только затем добавляет новое. То есть этот метод гарантирует, что будет существовать только одно поле с именем name, в остальном он аналогичен .append:

Поля объекта formData можно перебирать, используя цикл for..of:

let formData = new FormData();
formData.append('key1', 'value1');
formData.append('key2', 'value2');
var s = '';

// Список пар ключ/значение
for(let [name, value] of formData) {
   s += `${name} = ${value}\n`; // key1=value1, потом key2=value2 
 }
alert(s);

Отправка формы с файлом

Объекты FormData всегда отсылаются с заголовком Content-Type: form/multipart, этот способ кодировки позволяет отсылать файлы. Таким образом, поля <input type="file"> тоже отправляются, как это и происходит в случае обычной формы.

Пример такой формы:

<h4>Отправка формы с с файлом</h4> 
<form id="formElem">
  <input type="text" name="firstName" value="John">
  Картинка: <input type="file" name="picture" accept="image/*">
  <input type="submit">
</form>
<p id="msg"></pm> 

<script>
  formElem.onsubmit = async (e) => {
    e.preventDefault();

    let response = await fetch('xhr/f1.php?n=2', {
      method: 'POST',
      body: new FormData(formElem)
    });

    document.getElementById("msg").innerHTML = await response.text();
  };
</script>

Отправка формы с Blob-данными

В примере ниже посылается изображение из <canvas> и ещё несколько полей, как форма, используя FormData:

<h4>Отправка формы с Blob-данными</h4> 

  <canvas id="canvasElem" width="256" height="192" style="border:1px solid"></canvas>

  <input type="button" value="Отправить" onclick="submit()">
  <p id="msg"></pm> 

  <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("picture", imageBlob, "image.png");

      let response = await fetch('xhr/f1.php?n=2', {
        method: 'POST',
        body: formData
      });

      document.getElementById("msg").innerHTML = await response.text();
    }

  </script>

Пожалуйста, обратите внимание на то, как добавляется изображение Blob:

formData.append("image", imageBlob, "image.png");

Это как если бы в форме был элемент <input type="file" name="image"> и пользователь прикрепил бы файл с именем "image.png" (3й аргумент) и данными imageBlob (2й аргумент) из своей файловой системы.

Сервер прочитает и данные и файл, точно так же, как если бы это была обычная отправка формы.