Javascript свойство xmlhttprequest.status

Кодировка multipart/form-data

Кодировка urlencoded за счёт замены символов на %код может сильно «раздуть» общий объём пересылаемых данных. Поэтому для пересылки файлов используется другая кодировка: multipart/form-data.

В этой кодировке поля пересылаются одно за другим, через строку-разделитель.

Чтобы использовать этот способ, нужно указать его в атрибуте enctype и метод должен быть POST:

                        
  <form action="/submit" method="POST" enctype="multipart/form-data"> 
<input name="name" value="Виктор"> <input name="surname" value="Цой"> 
</form>

То есть, поля передаются одно за другим, значения не кодируются, а чтобы было чётко понятно, какое значение где – поля разделены случайно сгенерированной строкой, которую называют «boundary» (англ. граница), в примере выше это RaNdOmDeLiMiTeR:

Сервер видит заголовок Content-Type: multipart/form-data, читает из него границу и раскодирует соответсвенно  поля формы.

Такой способ можно использовать в первую очередь при пересылке файлов, так перекодировка мегабайтов через urlencoded существенно загрузила бы браузер. Да и объём данных после неё сильно вырос бы.

Однако, никто не мешает вам использовать эту кодировку всегда для POST запросов. Для GET доступна только urlencoded.

JS Учебник

JS ГлавнаяJS ВведениеJS Что? Где? Куда?JS ВыводJS ЗаявленияJS СинтаксисJS КомментарииJS ПеременныеJS ОператорыJS АрифметикаJS ПрисваиванияJS Типы данныхJS ФункцииJS ОбъектыJS СобытияJS СтрокиJS Методы строкJS ЧислаJS Методы чиселJS МассивыJS Методы массиваJS Сортировка массиваJS Итерация массиваJS Объекты датJS Формат датJS Метод получения датJS Метод набора датJS Математические…JS Случайные числаJS БулевыJS Сравнение…JS Заявления if…elseJS Заявление switchJS Цикл forJS Цикл whileJS Заявление break…JS Преобразование…JS Битовые…JS Регулярные выраженияJS ОшибкиJS ОбластьJS ПодъемныйJS СтрогийJS Ключевое слово thisJS Ключевое слово letJS КонстантыJS Функция стрелкиJS КлассыJS ОтладчикJS Руководство стиляJS ПрактикаJS Распространенные ошибкиJS ЭффективностьJS Зарезервированные словаJS ВерсииJS Версия ES5JS Версия ES6JS Версия 2016JS Версия 2017JS JSON

Пример использования

<!DOCTYPE html>
<html>
	<head>
		<title>Использование JavaScript методов open() и send() объекта XMLHttpRequest</title>
	</head>
	<body>
		<button onclick = "getPhone()">Запросить телефон</button> <!-- добавляем атрибут событий onclick -->
		<div id = "demo"></div>
		<script>
	function getPhone() {
	  let xhr = new XMLHttpRequest(); // инициализируем переменную новым объектом XMLHttpRequest
	  xhr.open("GET", "user.json"); // определяем параметры для запроса 
	  xhr.send(); // отправляем запрос на сервер

	  xhr.onreadystatechange = function() {
	    // проверяем состояние запроса и числовой код состояния HTTP ответа
	    if (this.readyState == 4 && this.status == 200) {
	      const data = JSON.parse(this.responseText); // анализируем строку в формате JSON и инициализируем переменную значением, полученным в ходе анализа
	      document.getElementById("demo").innerHTML = "Телефон пользователя: " + data.phone; // находим элемент по id и изменяем его содержимое значением ключа объекта, содержащегося в переменной
	    }
	  }; 
	}
		</script>
	</body>
</html>

В этом примере с использованием атрибута событий onclick при нажатии на кнопку (HTML элемент <button>) вызываем функцию getPhone, которая:

Вызывает конструктор объекта XMLHttpRequest и инициализирует переменную новым объектом этого типа.
С помощью метода open() объекта XMLHttpRequest определяем параметры для запроса — указываем, что HTTP запрос будет осуществлен методом «GET», а в качестве URL адреса на который будет отправлен запрос мы задаем файл формата json

Обратите внимание, что файл размещен на том же сервере и в том же каталоге, что и документ с которого выполняется скрипт. Файл имеет следующий вид:

{
«firstName»: «Василий»,
«lastName»: «Джейсонов»,
«age»: 25,
«phone»: 88005553535
}

С помощью метода send() объекта XMLHttpRequest отправляем запрос на сервер.
С использованием обработчика событий onreadystatechange, вызываемого при запуске события readystatechange, то есть при каждом изменении свойства readyState объекта XMLHttpRequest мы вызываем функцию, которая проверяет состояние запроса, оно должно соответствовать значению 4 (операция полностью завершена) и числовой код состояния HTTP ответа (свойство status) должен соответствовать значению 200 (успешный запрос)

Если условия выполнены, то с использованием метода JSON.parse() анализируем строку в формате JSON и инициализируем переменную значением, полученным в ходе анализа. После этого с помощью метода getElementById() находим элемент с определенным глобальным атрибутом id и изменяем его содержимое значением ключа объекта, содержащегося в инициализированной ранее переменной.

Результат нашего примера:


Пример использования методов open() и send() объекта XMLHttpRequestJavaScript XMLHttpRequest

Описание интерфейса

Интерфейс XMLHttpRequest это интерфейс, который предоставляет функциональность для обмена данными между клиентом и сервером, он дает возможность получения данных по URL без необходимости полного обновления страницы. Интерфейс XMLHttpRequest широко используется в асинхронных запросах AJAX (от англ. Asynchronous Javascript and XML — «асинхронный JavaScript и XML»), что позволяет конкретной странице обновлять только её часть, не нарушая при этом работу пользователя. Кроме того, XMLHttpRequest имеет широкое использование при разработке одностраничных приложений (англ. single page application, SPA).

Несмотря на свое название, XMLHttpRequest можно использовать для получения любого типа данных, а не только XML, он поддерживает протоколы, отличные от HTTP (включая file и ftp).

§Brief History of XHR

Despite its name, XHR was never intended to be tied to XML
specifically. The XML prefix is a vestige of a decision to ship the first
version of what became known as XHR as part of the MSXML library in
Internet Explorer 5:

Mozilla modeled its own implementation of XHR against Microsoft’s and
exposed it via the XMLHttpRequest interface. Safari, Opera, and other
browsers followed, and XHR became a de facto standard in all major
browsers—hence the name and why it stuck. In fact, the official W3C
Working Draft specification for XHR was only published in 2006, well
after XHR came into widespread use!

However, despite its popularity and key role in the AJAX revolution,
the early versions of XHR provided limited capabilities: text-based-only
data transfers, restricted support for handling uploads, and inability to
handle cross-domain requests. To address these shortcomings, the
«XMLHttpRequest Level 2» draft was published in 2008, which added a
number of new features:

  • Support for request timeouts

  • Support for binary and text-based data transfers

  • Support for application override of media type and encoding of
    responses

  • Support for monitoring progress events of each request

  • Support for efficient file uploads

  • Support for safe cross-origin requests

In 2011, «XMLHttpRequest Level 2» specification was merged with the
original XMLHttpRequest working draft. Hence, while you will often find
references to XHR version or level 1 and 2, these distinctions are no
longer relevant; today, there is only one, unified XHR specification. In
fact, all the new XHR2 features and capabilities are offered via the same
XMLHttpRequest API: same interface, more features.

Определение и применение

JavaScript свойство responseType объекта XMLHttpRequest возвращает перечисляемое строковое значение, указывающее тип данных, содержащихся в ответе. Допускается устанавливать responseType в определенное значение, при это вы должны убедиться, что сервер действительно отправляет ответ, совместимый с этим форматом. Если сервер возвращает данные, несовместимые с установленным типом ответа, то значение ответа будет равно null.

Если в качестве значения responseType задана пустая строка, то используется значение по умолчанию «text».

Типы возможных ответов представлены ниже:

Значение Тип данных свойства response
«» Пустая строка обрабатывается так же как значение «text», которое является значением по умолчанию.
«arraybuffer» Тип данных свойства response представляет собой JavaScript объект ArrayBuffer, содержащий двоичные данные.
«blob» Тип данных свойства response представляет собой JavaScript объект Blob, содержащий двоичные данные.
«document» Тип данных свойства response представляет собой документ HTML или XML, в зависимости от MIME типа полученных данных.
«json» Тип данных свойства response представляет собой объект JavaScript, созданный путем анализа содержимого полученных данных как JSON.
«text» Тип данных свойства response представляет собой текст в объекте DOMString. Это значение по умолчанию.

Изменить значение responseType в синхронном XMLHttpRequest можно только в том случае, если запрос принадлежит Worker. Это ограничение частично предназначено для обеспечения того, чтобы синхронные операции не использовались для больших операций, которые блокируют основной поток браузера, и деструктивны для пользовательского интерфейса.

XMLHttpRequest асинхронны по умолчанию, для перехода в синхронный режим, необходимо передать логическое значение false как значение необязательного параметра async при вызове метода open(), который позволяет инициализировать запрос. Большинство современных браузеров полностью упразднили синхронную поддержку XHR в основном потоке. Попытки установить значение responseType в «document» игнорируются в Worker.

Определение и применение

JavaScript свойство readyState объекта XMLHttpRequest возвращает состояние объекта XMLHttpRequest. Объект может находиться в следующих состояниях:

Значение Состояние Описание
UNSENT Объект XMLHttpRequest был создан, метод open() объекта не вызывался.
1 OPENED Метод open() объекта был вызван. Во время этого состояния заголовки запросов могут быть установлены с помощью метода setRequestHeader() и метод send() может быть вызван, который инициирует отправку запроса.
2 HEADERS_RECEIVED Метод send() объекта был вызван, заголовки и статус доступны.
3 Происходит загрузка тела ответа сервера. Если значение свойства responseType соответствует значению «text» или пустой строке, то значение свойства responseText будет содержать частичные данные.
4 DONE Операция завершена. Это может означать, что передача данных была завершена успешно или не удалась.

Обращаю Ваше внимание на то, что названия состояний отличаются в браузерах Internet Explorer в версиях ниже 11. Вместо UNSENT (0), OPENED (1), HEADERS_RECEIVED (2), (3) и DONE (4) используются такие названия как:

  • READYSTATE_UNINITIALIZED (0).
  • READYSTATE_LOADING (1).
  • READYSTATE_LOADED (2).
  • READYSTATE_INTERACTIVE (3).
  • READYSTATE_COMPLETE (4).

Пример использования

<!DOCTYPE html>
<html>
	<head>
		<title>Использование JavaScript методов open() и send() объекта XMLHttpRequest</title>
	</head>
	<body>
		<button onclick = "getPhone()">Запросить телефон</button> <!-- добавляем атрибут событий onclick -->
		<div id = "demo"></div>
		<script>
	function getPhone() {
	  let xhr = new XMLHttpRequest(); // инициализируем переменную новым объектом XMLHttpRequest
	  xhr.open("GET", "user.json"); // определяем параметры для запроса 
	  xhr.send(); // отправляем запрос на сервер

	  xhr.onreadystatechange = function() {
	    // проверяем состояние запроса и числовой код состояния HTTP ответа
	    if (this.readyState == 4 && this.status == 200) {
	      const data = JSON.parse(this.responseText); // анализируем строку в формате JSON и инициализируем переменную значением, полученным в ходе анализа
	      document.getElementById("demo").innerHTML = "Телефон пользователя: " + data.phone; // находим элемент по id и изменяем его содержимое значением ключа объекта, содержащегося в переменной
	    }
	  }; 
	}
		</script>
	</body>
</html>

В этом примере с использованием атрибута событий onclick при нажатии на кнопку (HTML элемент <button>) вызываем функцию getPhone, которая:

Вызывает конструктор объекта XMLHttpRequest и инициализирует переменную новым объектом этого типа.
С помощью метода open() объекта XMLHttpRequest определяем параметры для запроса — указываем, что HTTP запрос будет осуществлен методом «GET», а в качестве URL адреса на который будет отправлен запрос мы задаем файл формата json

Обратите внимание, что файл размещен на том же сервере и в том же каталоге, что и документ с которого выполняется скрипт. Файл имеет следующий вид:

{
«firstName»: «Василий»,
«lastName»: «Джейсонов»,
«age»: 25,
«phone»: 88005553535
}

С помощью метода send() объекта XMLHttpRequest отправляем запрос на сервер.
С использованием обработчика событий onreadystatechange, вызываемого при запуске события readystatechange, то есть при каждом изменении свойства readyState объекта XMLHttpRequest мы вызываем функцию, которая проверяет состояние запроса, оно должно соответствовать значению 4 (операция полностью завершена) и числовой код состояния HTTP ответа (свойство status) должен соответствовать значению 200 (успешный запрос)

Если условия выполнены, то с использованием метода JSON.parse() анализируем строку в формате JSON и инициализируем переменную значением, полученным в ходе анализа. После этого с помощью метода getElementById() находим элемент с определенным глобальным атрибутом id и изменяем его содержимое значением ключа объекта, содержащегося в инициализированной ранее переменной.

Результат нашего примера:


Пример использования методов open() и send() объекта XMLHttpRequestJavaScript XMLHttpRequest

Кросс-доменные запросы

Давайте рассмотрим  кросс-доменные запросы на примере кода:

// (1) 
var XHR = ("onload" in new XMLHttpRequest()) ? XMLHttpRequest : XDomainRequest; 
var xhr = new XHR(); 
// (2) запрос на другой домен :) 
xhr.open('GET', 'http://anywhere.com/request', true); 
xhr.onload = function() { alert( this.responseText ); } 
xhr.onerror = function() { alert( 'Ошибка ' + this.status ); } 
xhr.send();
  1. Мы создаём XMLHttpRequest и проверяем, поддерживает ли он событие onload. Если нет, то это старый XMLHttpRequest, значит это IE8,9, и надо использовать XDomainRequest.
  2. Запрос на другой домен отсылается просто указанием соответствующего URL в open. Он обязательно должен быть асинхронным, в остальном – никаких особенностей.

GET-запрос

Формируя XMLHttpRequest, надо формировать запрос «руками», кодируя поля функцией encodeURIComponent.

Например, для посылки GET-запроса с параметрами name и surname, аналогично форме выше, их необходимо закодировать так:

// Передаём name и surname в параметрах запроса 
var xhr = new XMLHttpRequest(); 
var params = 'name=' + encodeURIComponent(name) + '&surname=' + encodeURIComponent(surname); 
xhr.open("GET", '/submit?' + params, true); 
xhr.onreadystatechange = ...; 
xhr.send();

Прочие заголовки

Браузер автоматически добавит к запросу нужные HTTP-заголовки, такие как Content-Length и Connection.

По спецификации браузер запрещает их явную установку, как и некоторых других низкоуровневых HTTP-заголовков, которые могли бы ввести в заблуждение сервер относительно того, кто и сколько данных ему прислал, например Referer. Это делается в целях контроля правильности запроса и для безопасности.

Сообщаем про AJAX

Запрос, отправленный кодом выше через XMLHttpRequest, никак не отличается от обычной отправки формы. Сервер не в состоянии их отличить.
Поэтому в некоторых фреймворках, чтобы сказать серверу, что это AJAX, добавляют специальный заголовок:

xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");

Synchronous requests

If in the method the third parameter is set to , the request is made synchronously.

In other words, JavaScript execution pauses at and resumes when the response is received. Somewhat like or commands.

Here’s the rewritten example, the 3rd parameter of is :

It might look good, but synchronous calls are used rarely, because they block in-page JavaScript till the loading is complete. In some browsers it becomes impossible to scroll. If a synchronous call takes too much time, the browser may suggest to close the “hanging” webpage.

Many advanced capabilities of , like requesting from another domain or specifying a timeout, are unavailable for synchronous requests. Also, as you can see, no progress indication.

Because of all that, synchronous requests are used very sparingly, almost never. We won’t talk about them any more.

Upload progress

The event triggers only on the downloading stage.

That is: if we something, first uploads our data (the request body), then downloads the response.

If we’re uploading something big, then we’re surely more interested in tracking the upload progress. But doesn’t help here.

There’s another object, without methods, exclusively to track upload events: .

It generates events, similar to , but triggers them solely on uploading:

  • – upload started.
  • – triggers periodically during the upload.
  • – upload aborted.
  • – non-HTTP error.
  • – upload finished successfully.
  • – upload timed out (if property is set).
  • – upload finished with either success or error.

Example of handlers:

Here’s a real-life example: file upload with progress indication:

§Downloading Data with XHR

XHR can transfer both text-based and binary data. In fact, the browser
offers automatic encoding and decoding for a variety of native data
types, which allows the application to pass these types directly to XHR
to be properly encoded, and vice versa, for the types to be automatically
decoded by the browser:

Fixed-length binary data buffer

Binary large object of immutable data

Parsed HTML or XML document

JavaScript object representing a simple data structure

A simple text string

Either the browser can rely on the HTTP content-type negotiation to
infer the appropriate data type (e.g., decode an
application/json response into a JSON object), or the
application can explicitly override the data type when initiating the XHR
request:

var xhr = new XMLHttpRequest();
xhr.open('GET', '/images/photo.webp');
xhr.responseType = 'blob'; 

xhr.onload = function() {
  if (this.status == 200) {
    var img = document.createElement('img');
    img.src = window.URL.createObjectURL(this.response); 
    img.onload = function() {
        window.URL.revokeObjectURL(this.src); 
    }
    document.body.appendChild(img);
  }
};

xhr.send();
  1. Set return data type to blob

  2. Create unique object URI from blob and set as image source

  3. Release the object URI once image is loaded

Note that we are transferring an image asset in its native format,
without relying on base64 encoding, and adding an image element to the
page without relying on data URIs. There is no network transmission
overhead or encoding overhead when handling the received binary data in
JavaScript! XHR API allows us to script efficient, dynamic applications,
regardless of the data type, right from JavaScript.

Зачем нужен CORS? Экскурс в историю

CORS существует для защиты интернета от злых хакеров.

Серьёзно. Давайте сделаем краткое историческое отступление.

Многие годы скрипт с одного сайта не мог получить доступ к содержимому другого сайта.

Это простое, но могучее правило было основой интернет-безопасности. Например, хакерский скрипт с сайта не мог получить доступ к почтовому ящику пользователя на сайте . И люди чувствовали себя спокойно.

В то время в JavaScript не было методов для сетевых запросов. Это был «игрушечный» язык для украшения веб-страниц.

Но веб-разработчики жаждали большей власти. Чтобы обойти этот запрет и всё же получать данные с других сайтов, были придуманы разные хитрости.

Одним из способов общения с другим сервером была отправка туда формы . Люди отправляли её в , чтобы оставаться на текущей странице, вот так:

Таким способом было возможно сделать GET/POST запрос к другому сайту даже без сетевых методов, так как формы можно отправлять куда угодно. Но так как запрещено получать доступ к содержимому с другого сайта, прочитать ответ было невозможно.

Если быть точным, были трюки и для этого, требующие специального кода на странице и в ифрейме, так что общение с ифреймом было технически возможно. Сейчас мы не будем вдаваться в подробности, пусть эти динозавры покоятся в мире.

Ещё один трюк заключался в использовании тега . У него может быть любой , с любым доменом, например . Это даёт возможность загрузить и выполнить скрипт откуда угодно.

Если сайт, например , хотел предоставить данные для такого доступа, он предоставлял так называемый «протокол JSONP» (JSON with Padding)».

Вот как он работал.

Например, нам на нашем сайте нужны данные с сайта , скажем, погода:

  1. Сначала, заранее, объявляем глобальную функцию для обработки данных, например .

  2. Затем создаём тег с , при этом имя нашей функции – в URL-параметре .

  3. Удалённый сервер с должен в ответ сгенерировать скрипт, который вызывает с данными, которые хочет передать.

  4. Когда этот скрипт загрузится и выполнится, наша функция получает данные.

Это работает и не нарушает безопасность, потому что обе стороны согласились передавать данные таким образом. А когда обе стороны согласны, то это определённо не хак. Всё ещё существуют сервисы, которые предоставляют такой доступ, так как это работает даже для очень старых браузеров.

Спустя некоторое время в браузерном JavaScript появились методы для сетевых запросов.

Вначале запросы на другой источник были запрещены. Но в результате долгих дискуссий было решено разрешить их делать, но для использования новых возможностей требовать разрешение сервера, выраженное в специальных заголовках.

§XHR Use Cases and Performance

XMLHttpRequest is what enabled us to make the leap from building pages
to building interactive web applications in the browser. First, it
enabled asynchronous communication within the browser, but just as
importantly, it also made the process simple. Dispatching and controlling
a scripted HTTP request takes just a few lines of JavaScript code, and
the browser handles all the rest:

  • Browser formats the HTTP request and parses the response.

  • Browser enforces relevant security (same-origin) policies.

  • Browser handles content negotiation (e.g., gzip).

  • Browser handles request and response caching.

  • Browser handles authentication, redirects, and more…

As such, XHR is a versatile and a high-performance transport for any
transfers that follow the HTTP request-response cycle. Need to fetch a
resource that requires authentication, should be compressed while in
transfer, and should be cached for future lookups? The browser takes care
of all of this and more, allowing us to focus on the application logic!

However, XHR also has its limitations. As we saw, streaming has never
been an official use case in the XHR standard, and the support is
limited: streaming with XHR is neither efficient nor convenient.
Different browsers have different behaviors, and efficient binary
streaming is impossible. In short, XHR is not a good fit for streaming.

Similarly, there is no one best strategy for delivering real-time
updates with XHR. Periodic polling incurs high overhead and message
latency delays. Long-polling delivers low latency but still has the same
per-message overhead; each message is its own HTTP request. To have both
low latency and low overhead, we need XHR streaming!

As a result, while XHR is a popular mechanism for «real-time»
delivery, it may not be the best-performing transport for the job. Modern
browsers support both simpler and more efficient options, such as
Server-Sent Events and WebSocket. Hence, unless you have a specific
reason why XHR polling is required, use them.

Other signatures


  • a simple string instead of the options. In this case, a GET request will be made to that url.


  • the above may also be called with the standard set of options.

The module has convience functions attached that will make requests with the given method.
Each function is named after its method, with the exception of which is called for compatibility.

The method shorthands may be combined with the url-first form of for succinct and descriptive requests. For example,

xhr.post('/post-to-me',function(err,resp){console.log(resp.body)})

or

xhr.del('/delete-me',{ headers{ my'auth'}},function(err,resp){console.log(resp.statusCode);})

Обмен ресурсами с запросом происхождения (Cross Origin Resource Sharing, или CORS)

С помощью технологии CORS веб-приложения могут выполнять кросс-доменные AJAX-запросы к другим доменам. Сделать это очень просто: достаточно, чтобы сервер отправил необходимый заголовок ответа.

Включение CORS-запросов

Предположим, приложение находится в домене и нужно получить данные из домена . Как правило, при попытке отправить такой AJAX-запрос он не выполняется, а браузер выдает ошибку несоответствия происхождения. Благодаря технологии CORS сайт может разрешить приложению с сайта выполнять запросы путем добавления одного заголовка.

Access-Control-Allow-Origin: http://example.com

Заголовок можно добавить как для одного сайта, так и для всего домена. Чтобы разрешить отправку запросов из всех доменов, добавьте строку такого вида:

Access-Control-Allow-Origin: *

Фактически на всех страницах этого сайта (html5rocks.com) также используется технология CORS. Запустите инструменты разработчика, и в ответе вы увидите заголовок :

Заголовок на сайте html5rocks.com

Разрешить кросс-доменные запросы несложно, поэтому настоятельно рекомендуется включать CORS для общедоступных данных.

Создание кросс-доменного запроса

Если сервер-адресат поддерживает CORS, кросс-доменный запрос ничем не отличается от обычного запроса . Например, вот так можно выполнить запрос с приложения на сервере к серверу :

var xhr = new XMLHttpRequest();
xhr.open('GET', 'http://www.example2.com/hello.json');
xhr.onload = function(e) {
  var data = JSON.parse(this.response);
  ...
}
xhr.send();

Другие ресурсы

События и также срабатывают и для других ресурсов, а вообще, для любых ресурсов, у которых есть внешний .

Например:

let img = document.createElement('img');
img.src = "https://js.cx/clipart/train.gif"; // (*)

img.onload = function() {
  alert(`Изображение загружено, размеры ${img.width}x${img.height}`);
};

img.onerror = function() {
  alert("Ошибка во время загрузки изображения");
};

Однако есть некоторые особенности:

  • Большинство ресурсов начинают загружаться после их добавления в документ. За исключением тега . Изображения начинают загружаться, когда получают .
  • Для событие срабатывает по окончании загрузки как в случае успеха, так и в случае ошибки.

Такое поведение сложилось по историческим причинам.

§Uploading Data with XHR

Uploading data via XHR is just as simple and efficient for all data
types. In fact, the code is effectively the same, with the only
difference that we also pass in a data object when calling
send() on the XHR request. The rest is handled by the browser:

var xhr = new XMLHttpRequest();
xhr.open('POST','/upload');
xhr.onload = function() { ... };
xhr.send("text string"); 

var formData = new FormData(); 
formData.append('id', 123456);
formData.append('topic', 'performance');

var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.onload = function() { ... };
xhr.send(formData); 

var xhr = new XMLHttpRequest();
xhr.open('POST', '/upload');
xhr.onload = function() { ... };
var uInt8Array = new Uint8Array(); 
xhr.send(uInt8Array.buffer); 
  1. Upload a simple text string to the server

  2. Create a dynamic form via FormData API

  3. Upload multipart/form-data object to the server

  4. Create a typed array (ArrayBuffer) of unsigned, 8-bit integers

  5. Upload chunk of bytes to the server

The XHR send() method accepts one of ,
, , ,
, or objects, automatically
performs the appropriate encoding, sets the appropriate HTTP
content-type, and dispatches the request. Need to send a binary blob or
upload a file provided by the user? Simple: grab a reference to the
object and pass it to XHR. In fact, with a little extra work, we can also
split a large file into smaller chunks:

var blob = ...; 

const BYTES_PER_CHUNK = 1024 * 1024; 
const SIZE = blob.size;

var start = 0;
var end = BYTES_PER_CHUNK;

while(start < SIZE) { 
  var xhr = new XMLHttpRequest();
  xhr.open('POST', '/upload');
  xhr.onload = function() { ... };

  xhr.setRequestHeader('Content-Range', start+'-'+end+'/'+SIZE); 
  xhr.send(blob.slice(start, end)); 

  start = end;
  end = start + BYTES_PER_CHUNK;
}
  1. An arbitrary blob of data (binary or text)

  2. Set chunk size to 1 MB

  3. Iterate over provided data in 1MB increments

  4. Advertise the uploaded range of data (start-end/total)

  5. Upload 1 MB slice of data via XHR

XHR does not support request streaming, which means that we must
provide the full payload when calling send(). However, this
example illustrates a simple application workaround: the file is split
and uploaded in chunks via multiple XHR requests. This implementation
pattern is by no means a replacement for a true request streaming API,
but it is nonetheless a viable solution for some applications.

Событие onprogress в деталях

При обработке события onprogress есть ряд важных тонкостей.

Заметим, что событие, возникающее при onprogress, имеет одинаковый вид на стадии закачки (в обработчике xhr.upload.onprogress) и при получении ответа (в обработчике xhr.onprogress).

Оно представляет  объект типа ProgressEvent со свойствами:

loaded
Сколько байт уже переслано.

Имеется в виду только тело запроса, заголовки не учитываются.

lengthComputable
Если true, то известно полное количество байт для пересылки, и оно хранится в свойстве total.
total
Общее количество байт для пересылки, если известно.

А может ли оно быть неизвестно?

  • При закачке на сервер браузер всегда знает полный размер пересылаемых данных, так что total всегда содержит конкретное количество байт, а значение lengthComputable всегда будет true.
  • При скачивании данных – обычно сервер в начале сообщает их общее количество в HTTP-заголовке Content-Length. Но он может и не делать этого, например если сам не знает, сколько данных будет или если генерирует их динамически. Тогда total будет равно нулю. А чтобы отличить нулевой размер данных от неизвестного – как раз служит lengthComputable, которое в данном случае равно false.

Ещё особенности, которые необходимо учитывать при использовании onprogress:

Событие происходит при каждом полученном/отправленном байте, но не чаще чем раз в 50 мс.Это обозначено в спецификации progress notifications.
В процессе получения данных, ещё до их полной передачи, доступен xhr.responseText, но он не обязательно содержит корректную строку.Можно до окончания запроса заглянуть в него и прочитать текущие полученные данные

Важно, что при пересылке строки в кодировке UTF-8 кириллические символы, как, впрочем, и многие другие, кодируются 2 байтами. Возможно, что в конце одного пакета данных окажется первая половинка символа, а в начале следующего – вторая

Поэтому полагаться на то, что до окончания запроса в responseText находится корректная строка нельзя. Она может быть обрезана посередине символа.Исключение – заведомо однобайтные символы, например цифры или латиница.
Сработавшее событие xhr.upload.onprogress не гарантирует, что данные дошли.Событие xhr.upload.onprogress срабатывает, когда данные отправлены браузером. Но оно не гарантирует, что сервер получил, обработал и записал данные на диск. Он говорит лишь о самом факте отправки.Поэтому прогресс-индикатор, получаемый при его помощи, носит приблизительный и оптимистичный характер.

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

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

Adblock
detector