Multipart form data что это
Перейти к содержимому

Multipart form data что это

  • автор:

Что такое multipart/form-data?

Появилась потребность залить файлы через на vk-сервер через его API. В справке по работе с API сказано:

файлы должны передаваться в формате multipart/form-data на url сервер

Однако, не понятно что такое multiform и не понятно как передавать эти файлы. Документацию в гугле не нашел. Прошу тыкнуть ссылкой. Единственное что, прочитал статью на вики про multiform. Там указан пример формирования формы, однако как её отправлять и сформировать я не нашёл.

Отслеживать

67.9k 216 216 золотых знаков 77 77 серебряных знаков 219 219 бронзовых знаков

задан 15 янв 2016 в 16:43

Nikita Kulikov Nikita Kulikov

73 2 2 серебряных знака 5 5 бронзовых знаков

FormData

В этой главе речь пойдёт об отправке HTML-форм: с файлами и без, с дополнительными полями и так далее. Объекты FormData помогут нам с этим. Как вы, наверняка, догадались по его названию, это объект, представляющий данные HTML формы.

let formData = new FormData([form]);

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

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

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

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

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

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

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

    

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

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

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

  • formData.append(name, value) – добавляет к объекту поле с именем name и значением value ,
  • formData.append(name, blob, fileName) – добавляет поле, как будто в форме имеется элемент , третий аргумент 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) .

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

let formData = new FormData(); formData.append('key1', 'value1'); formData.append('key2', 'value2'); // Список пар ключ/значение for(let [name, value] of formData) < alert(`$= $`); // key1=value1, потом key2=value2 >

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

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

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

 Картинка:   

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

Ранее в главе Fetch мы видели, что очень легко отправить динамически сгенерированные бинарные данные в формате Blob . Мы можем явно передать её в параметр body запроса fetch .

Но на практике бывает удобнее отправлять изображение не отдельно, а в составе формы, добавив дополнительные поля для имени и другие метаданные.

Кроме того, серверы часто настроены на приём именно форм, а не просто бинарных данных.

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

    

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

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

Это как если бы в форме был элемент и пользователь прикрепил бы файл с именем «image.png» (3-й аргумент) и данными imageBlob (2-й аргумент) из своей файловой системы.

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

Итого

Объекты 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)

Отметим две особенности:

  1. Метод set удаляет предыдущие поля с таким же именем, а append – нет. В этом их единственное отличие.
  2. Чтобы послать файл, нужно использовать синтаксис с тремя аргументами, в качестве третьего как раз указывается имя файла, которое обычно, при , берётся из файловой системы.
  • formData.delete(name)
  • formData.get(name)
  • formData.has(name)

Form Data

Асинхронно отправлять JSON на сервер может каждый, а файлы — только Form Data .

Время чтения: 8 мин

Открыть/закрыть навигацию по статье

Обновлено 5 августа 2022

Кратко

Скопировать ссылку «Кратко» Скопировано

Form Data — это специальная коллекция данных, которая позволяет передавать данные в виде пар [ключ , значение ] на сервер при помощи fetch ( ) или XML Http Request . При этом используется точно такой же формат данных, какой использует тег с типом кодирования ‘multipart / form — data’ . Поэтому, значения в Form Data , как и у обычной HTML формы, могут быть только строками или файлами.

Пример

Скопировать ссылку «Пример» Скопировано

Предположим, что мы пишем функцию, которая отправляет на сервер два поля name и email . Значения полей она получает через аргументы:

 async function sendData(name, email)  const data = new FormData() data.append('name', name) data.append('email', email) return await fetch('/api/subscribe/',  method: 'POST', body: data, >)> async function sendData(name, email)  const data = new FormData() data.append('name', name) data.append('email', email) return await fetch('/api/subscribe/',  method: 'POST', body: data, >) >      

Данные отправляются на сервер с помощью объекта Form Data . Мы используем метод append ( ) чтобы добавить значения, а затем передаём полученный объект функции fetch ( ) .

Как пишется

Скопировать ссылку «Как пишется» Скопировано

Для работы с Form Data сначала с помощью конструктора new создаётся объект этого типа: const form = new Form Data ( ) . Затем у полученного объекта можно вызывать методы.

Основные методы для работы с Form Data :

  • append ( ключ , значение ) — добавляет значение для ключа с сохранением предыдущих значений;
  • set ( ключ , значение ) — устанавливает значение для ключа, перезаписывая предыдущие значения;
  • get ( ключ ) — возвращает первое значение ключа;
  • get All ( ключ ) — возвращает все значения ключа;
  • has ( ключ ) — проверяет наличие переданного ключа;
  • entries ( ) — возвращает итератор пар [ключ , значение ] ;
  • values ( ) — возвращает итератор всех значений коллекции;
  • keys ( ) — возвращает итератор всех ключей коллекции;
  • delete ( ключ ) — удаляет конкретное значение;

Как понять

Скопировать ссылку «Как понять» Скопировано

При отправке данных на сервер и сервер и клиент должны понимать друг друга, то есть использовали понятные обоим способы кодирования и декодирования данных. Таких способов существует большое количество и Form Data позволяет работать с одним из них — ‘multipart / form — data’ .

Form Data похожа на коллекцию Map — предоставляет удобные методы для добавления и удаления данных. Но если передать её в качестве тела запроса при вызове fetch ( ) (как в примере выше), данные «под капотом» будут преобразованы в нужный формат, а HTTP-заголовку Content — Type будет присвоено значение ‘multipart / form — data’ , чтобы сервер знал, что именно с этим форматом ему предстоит работать.

Form Data является отражением данных обычной HTML-формы с атрибутом enctype = «multipart / form — data» , поэтому пример выше можно представить следующим образом без JavaScript:

      form method="post" action="/api/subscribe/" enctype="multipart/form-data"> input type="text" name="name" value="" /> input type="email" name="email" value="" /> button type="submit">Отправитьbutton> form>      

Когда выбирать

Скопировать ссылку «Когда выбирать» Скопировано

Существует несколько самых популярных способов кодирования данных для отправки на сервер: ‘application / x — www — form — urlencoded’ , ‘multipart / form — data’ и ‘application / json’ . Иногда бывает так, что сервер поддерживает только какой-то определённый способ. Тогда выбирать не приходится. Но чаще всего современные решения на бэкенде поддерживают несколько способов, поэтому выбирать нужно в зависимости от задачи.

  • ‘application / x — www — form — urlencoded’ — способ, который используют HTML-формы по умолчанию. Из-за особенностей преобразования, этот способ плохо подходит для больших объёмов данных, в особенности, файлов или строк с большим количеством символов не из ASCII таблицы (например, символы русского алфавита).
  • ‘application / json’ — достаточно популярный формат из-за широкого распространения JSON как формата обмена данными. Из плюсов — поддерживает вложенные структуры, поэтому можно в одном запросе отправить, например, целый объект с данными. Однако, чтобы отправить файл при помощи этого формата, необходимо файл дополнительно закодировать в строку каким-нибудь алгоритмом, например Base64. Причём на сервере нужно декодировать эти данные обратно.
  • ‘multipart / form — data’ — удобный способ для загрузки файлов, оптимален с точки зрения размера закодированных данных, но в качестве значений может хранить только строки или файлы.

Поэтому, лучше всего использовать Form Data для отправки файлов на сервер или когда поддержка только строковых данных не является проблемой. Дополнительно, при создании Form Data можно передать DOM-элемент формы (будет рассмотрено ниже), и коллекция вытащит из этой формы все данные. Поэтому, если стоит задача отравить данные какой-либо формы, Form Data позволит сделать это с минимумом кода.

Создание Form Data

Скопировать ссылку «Создание FormData» Скопировано

Создать новый пустой объект Form Data можно с помощью конструктора:

 const data = new FormData() const data = new FormData()      

Также, конструктор может принимать в качестве аргумента DOM-элемент формы, в этом случае Form Data запишет текущие значения полей формы:

      form id="user-form"> input type="text" name="name" value="Аня" /> input type="text" name="language" value="JavaScript" /> form>      
 const form = document.querySelector('#user-form')const data = new FormData(form) for (let [key, value] of data)  console.log(`$ - $`)>// 'name - Аня'// 'language - JavaScript' const form = document.querySelector('#user-form') const data = new FormData(form) for (let [key, value] of data)  console.log(`$key> - $value>`) > // 'name - Аня' // 'language - JavaScript'      

Работа с коллекцией

Скопировать ссылку «Работа с коллекцией» Скопировано

Для добавления данных в коллекцию есть метод append ( ) :

 const data = new FormData()data.append('name', 'Вася') const data = new FormData() data.append('name', 'Вася')      

Теперь в коллекции появилось одно значение с ключом name и значением ‘Вася’.

Form Data может хранить несколько значений для одного ключа! Метод append ( ) , вызванный для ключа, по которому уже есть значение, добавит для этого ключа новые данные, не удаляя старые.

После выполнения этого кода, в коллекции будет два значения (‘Вася’ и ‘Лена’) для одного ключа name :

 const data = new FormData() data.append('name', 'Вася')data.append('name', 'Лена') const data = new FormData() data.append('name', 'Вася') data.append('name', 'Лена')      

Form Data поддерживает ещё один метод для записи данных: set ( ) . В отличие от append ( ) , он перезапишет старые данные для переданного ключа, если они были:

 const data = new FormData() data.set('name', 'Вася')data.set('name', 'Лена') const data = new FormData() data.set('name', 'Вася') data.set('name', 'Лена')      

В коллекции у ключа name будет одно значение ‘Лена’, потому что прошлое значение было перезаписано.

Form Data может хранить только строки или файлы в качестве значений. Все данные других типов будут преобразованы в строку.

Вот пример такого поведения. Записываем число 30 , но фактически записывается строка ’30’ :

 const data = new FormData() data.append('age', 30) console.log(data.get('age') === 30)// false console.log(data.get('age') === '30')// true console.log(typeof data.get('age'))// 'string' const data = new FormData() data.append('age', 30) console.log(data.get('age') === 30) // false console.log(data.get('age') === '30') // true console.log(typeof data.get('age')) // 'string'      

Для получения записанных значений есть два метода: get ( ) и get All ( ) . get ( ) вернёт первое значение для ключа или null , если для указанного ключа значений не было:

 const data = new FormData() console.log(data.get('name'))// null data.append('name', 'Вася')data.append('name', 'Лена')console.log(data.get('name'))// 'Вася' const data = new FormData() console.log(data.get('name')) // null data.append('name', 'Вася') data.append('name', 'Лена') console.log(data.get('name')) // 'Вася'      

В примере выше второе значение ‘Лена’ при помощи метода get ( ) недоступно, потому что он всегда возвращает только первое значение. Поэтому, чтобы получить все значения, на помощь приходит get All ( ) . Он всегда возвращает массив значений для указанного ключа. Если значений не было, возвращаемый массив будет пустой:

 const data = new FormData() console.log(data.getAll('name'))// [] data.append('name', 'Вася')data.append('name', 'Лена')console.log(data.getAll('name'))// ['Вася', 'Лена'] const data = new FormData() console.log(data.getAll('name')) // [] data.append('name', 'Вася') data.append('name', 'Лена') console.log(data.getAll('name')) // ['Вася', 'Лена']      

Чтобы проверить, есть ли в коллекции данные для определённого ключа, есть метод has ( ) , он вернёт true или false:

 const data = new FormData() console.log(data.has('name'))// false data.append('name', 'Вася') console.log(data.has('name'))// true const data = new FormData() console.log(data.has('name')) // false data.append('name', 'Вася') console.log(data.has('name')) // true      

Для удаления значений для определённого ключа можно воспользоваться методом delete ( ) . Важно помнить, что если у указанного ключа несколько значений, то удалятся все значения:

 const data = new FormData() data.append('name', 'Вася')data.append('name', 'Лена') data.delete('name') const data = new FormData() data.append('name', 'Вася') data.append('name', 'Лена') data.delete('name')      

После выполнения кода, коллекция снова будет пустая. Мы удалили ключ целиком, поэтому оба значения по этому ключу исчезли.

Обход значений

Скопировать ссылку «Обход значений» Скопировано

Form Data предоставляет встроенный итератор для обхода значений:

 const data = new FormData() data.append('name', 'Вася')data.append('name', 'Лена')data.append('language', 'JavaScript') for (let [key, value] of data)  console.log(`$ - $`)>// name - Вася// name - Лена// language - JavaScript const data = new FormData() data.append('name', 'Вася') data.append('name', 'Лена') data.append('language', 'JavaScript') for (let [key, value] of data)  console.log(`$key> - $value>`) > // name - Вася // name - Лена // language - JavaScript      

Тот же итератор доступен при помощи метода entries ( ) . Обратите внимание, что каждый элемент итератора — массив из двух элементов. Первый элемент — ключ, а второй — значение.

В дополнение к этому, Form Data предоставляет два других итератора: только ключей при помощи метода keys ( ) и только значений при помощи values ( ) . Каждый ключ при перечислении ключей будет появляться ровно столько раз, сколько значений он содержит:

 const data = new FormData() data.append('name', 'Вася')data.append('name', 'Лена')data.append('language', 'JavaScript') console.log('Проходимся по значениям:')for (let value of data.values())  console.log(value)>// 'Вася'// 'Лена'// 'JavaScript' console.log('Проходимся по ключам:')for (let key of data.keys())  console.log(key)>// 'name'// 'name' // 'language' const data = new FormData() data.append('name', 'Вася') data.append('name', 'Лена') data.append('language', 'JavaScript') console.log('Проходимся по значениям:') for (let value of data.values())  console.log(value) > // 'Вася' // 'Лена' // 'JavaScript' console.log('Проходимся по ключам:') for (let key of data.keys())  console.log(key) > // 'name' // 'name' // 'language'      

На практике

Скопировать ссылку «На практике» Скопировано

Виталий Баев советует

Скопировать ссылку «Виталий Баев советует» Скопировано

Сильной стороной Form Data является загрузка файлов на сервер. Если при использовании ‘application / json’ файлы необходимо дополнительно кодировать каким-то способом, чтобы привести к строке (и точно так-же декодировать на сервере), то Form Data умеет это делать «из коробки».

Например, если мы хотим после выбора файла сразу же загрузить его на сервер, то нам понадобится следующий HTML:

  input id="file-input" type="file" />      

А для отправки на сервер просто добавим файл в объект Form Data и отправим его на сервер:

 // Объявляем функцию загрузки файлаfunction sendFile(file)  const data = new FormData() // Добавляем файл data.append('document', file) return fetch('/api/upload/',  method: 'POST', body: data, >)> const fileInput = document.querySelector('#file-input')fileInput.addEventListener('change', (event) =>  // Получаем файл. Обратите внимание, что файлов может быть несколько если у инпута стоит атрибут `multiple` const file = event.target.files[0] // Отправляем файл на сервер при помощи созданной функции sendFile(file) // Очищаем текущее значение инпута. Если этого не делать, то при ошибке загрузки, повторный выбор того же файла не вызовет событие _change_ event.target.value = null>) // Объявляем функцию загрузки файла function sendFile(file)  const data = new FormData() // Добавляем файл data.append('document', file) return fetch('/api/upload/',  method: 'POST', body: data, >) > const fileInput = document.querySelector('#file-input') fileInput.addEventListener('change', (event) =>  // Получаем файл. Обратите внимание, что файлов может быть несколько если у инпута стоит атрибут `multiple` const file = event.target.files[0] // Отправляем файл на сервер при помощи созданной функции sendFile(file) // Очищаем текущее значение инпута. Если этого не делать, то при ошибке загрузки, повторный выбор того же файла не вызовет событие _change_ event.target.value = null >)      

В Доке есть отличная статья «Загрузка файла с прогресс-баром», где можно почитать подробнее про загрузку файлов с использованием Form Data .

Использование Объектов FormData

Объект FormData позволяет создать набор пар ключ/значение и передать их, используя XMLHttpRequest. Объект FormData предназначен для передачи данных форм, однако может быть использован для передачи пар ключ/значение независимо от форм. Данные передаются в том же формате, как и данные, передаваемые методом submit() формы, с установленной кодировкой enctype=»multipart/form-data» .

Создание объекта FormData

Вы можете самостоятельно создать пустой объект FormData , наполнив его затем данными, используя его метод append() :

var formData = new FormData(); formData.append("username", "Groucho"); formData.append("accountnum", 123456); // число 123456 немедленно преобразуется в строку "123456" // Файл, выбранный пользователем formData.append("userfile", fileInputElement.files[0]); // JavaScript Blob объект var content = 'hey!'; // содержимое нового файла. var blob = new Blob([content],  type: "text/xml" >); formData.append("webmasterfile", blob); var request = new XMLHttpRequest(); request.open("POST", "http://foo.com/submitform.php"); request.send(formData); 

Примечание: Поля «userfile» и «webmasterfile» оба содержат файлы. Число, переданное полю «accountnum» немедленно преобразуется в строку. Преобразование осуществляется методом FormData.append() (Значение поля может быть Blob , File , или строкой: если значение не является ни Blob, ни File, то оно автоматически преобразуется в строку).

Данный пример показывает создание экземпляра FormData , содержащего поля «username», «accountnum», «userfile» и «webmasterfile». Экземпляр FormData затем отправляется при помощи метода send() объекта XMLHttpRequest . Поле «webmasterfile» является экземпляром класса Blob . Объект класса Blob является файлом-подобным объектом, содержащим «сырые» данные. Определение данных как Blob не является обязательным в нативном javascript. Интерфейс File базируется на Blob , наследуя его функциональность и расширяя его для поддержки файлов в ОС пользователя. Для создания объектов класса Blob используйте Blob() constructor .

Получение объекта FormData из HTML формы

Для создания объекта FormDatа , содержащего данные существующей формы ( ) передайте форму в качестве аргумента при создании объекта FormData:

**Примечание:**FormData будет использовать только те поля ввода, которые используют атрибут name.

var formData = new FormData(someFormElement); 
var formElement = document.querySelector("form"); var request = new XMLHttpRequest(); request.open("POST", "submitform.php"); request.send(new FormData(formElement)); 

Вы так же можете добавить дополнительные данные в объект FormData после его создания и до отправки данных:

var formElement = document.querySelector("form"); var formData = new FormData(formElement); var request = new XMLHttpRequest(); request.open("POST", "submitform.php"); formData.append("serialnumber", serialNumber++); request.send(formData); 

Такой приём позволяет включать в форму дополнительную информацию, которая, например, не нуждается в демонстрации пользователю.

Отправка файлов при помощи объекта FormData

Вы так же можете отправлять файлы при помощи FormData . Просто включите с типом file в форму.

form enctype="multipart/form-data" method="post" name="fileinfo"> label>Your email address:label> input type="email" autocomplete="on" autofocus name="userid" placeholder="email" required size="32" maxlength="64" />br /> label>Custom file label:label> input type="text" name="filelabel" size="12" maxlength="32" />br /> label>File to stash:label> input type="file" name="file" required /> input type="submit" value="Stash the file!" /> form> div>div> 

Затем вы сможете отправить выбранный файл следующим образом:

var form = document.forms.namedItem("fileinfo"); form.addEventListener( "submit", function (ev)  var oOutput = document.querySelector("div"), oData = new FormData(form); oData.append("CustomField", "This is some extra data"); var oReq = new XMLHttpRequest(); oReq.open("POST", "stash.php", true); oReq.onload = function (oEvent)  if (oReq.status == 200)  oOutput.innerHTML = "Uploaded!"; > else  oOutput.innerHTML = "Error " + oReq.status + " occurred when trying to upload your file.
"
; > >; oReq.send(oData); ev.preventDefault(); >, false, );

Примечание: Если для формы указан атрибут method , то будет использован именно этот метод для отправки данных на сервер, а не метод, указанный в вызове open()

Вы так же можете добавить File или Blob непосредственно к объекту FormData :

.append("myfile", myBlob, "filename.txt"); 

Метод append() принимает 3й опциональный параметр — название файла, которое добавляется в заголовок Content-Disposition при отправке на сервер. Если название файла не указано (или данный параметр не поддерживается) используется имя «blob».

Вы так же можете использовать FormData с jQuery:

var fd = new FormData(document.querySelector("form")); fd.append("CustomField", "This is some extra data"); $.ajax( url: "stash.php", type: "POST", data: fd, processData: false, // Сообщить jQuery не передавать эти данные contentType: false, // Сообщить jQuery не передавать тип контента >); 

Отправка форм и файлов при помощи AJAX без использования объекта FormData

Если вы заинтересованы в отправке форм и файлов при помощи AJAX без использования FormData, прочитайте /ru/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest#Submitting_forms_and_uploading_files

See also

Found a content problem with this page?

  • Edit the page on GitHub.
  • Report the content issue.
  • View the source on GitHub.

This page was last modified on 3 авг. 2023 г. by MDN contributors.

Your blueprint for a better internet.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *