Javascript.RU

Multipart XMLHTTPRequest

Update: Более новый материал по этой теме находится по адресу https://learn.javascript.ru/xhr-onprogress.

Этот способ, как ни странно, поддерживается только Firefox. На момент написания Opera и Safari под Windows не поддерживали его. Он представляет собой XHR-запрос, ответ на который может быть разделен на части. При получении новой части вызывается onload().

Вот - пример, можете попробовать его в любом браузере, и увидите, изменилось ли что-то в поддержке. Эталонное поведение: Firefox.

Тут будут сообщения

Вот - клиентский код

function runMultipart() {
	// не кросс-браузерно, все равно способ Firefox only
	var req = new XMLHttpRequest(); 
	
	req.multipart = true;
	// асинхронный запрос
	req.open("GET","/server_push/multipart.php?r="+Math.random(), true);
	req.onload = function(event) {
		var result = event.target.responseText
		
		var d = document.createElement("div")
		d.innerHTML = "onload:"+result;

		document.getElementById('xhr_multipart_dump1').appendChild(d)
	}
	req.onreadystatechange = function() {
		if (req.readyState!=4) return
		var d = document.createElement("div")
		d.innerHTML = "State:"+req.readyState+' Status:'+req.status
		document.getElementById('xhr_multipart_dump1').appendChild(d)
	}
	
	req.send(null);
}

Асинхронность запроса в случае с Multipart XMLHTTPRequest - вещь довольно условная, при установке синхронности - реально браузер подвиснет только до
первой части multipart-сообщения. Думается, это самое правильное, что можно было сделать, т.к получать весь multipart-запрос синхронно никому не нужно, для этого
есть обычные запросы.

Для обработки данных, как видно из примера, в Multipart XMLHTTPRequest используется функция onload, которая получает последний кусок из event.

Есть аналогичная функция onerror, но при вызове из javascript она не работает. Поэтому для обработки ошибок используем старый добрый onreadystatechange, который
вызывается при каждом куске данных или при ошибке.

Чтобы ответить на Multipart XMLHTTPRequest, сервер должен выбрать уникальный разделитель, и первым делом - сообщить его браузеру вместе со спец. заголовком:

header('Content-type: multipart/x-mixed-replace;boundary="УнИкАлЬНЫЙРАздеЛитЕЛЬ"');

Далее сервер может слать данные, и информировать браузер о конце каждой части ответа посылкой разделителя. Перед каждой частью ответа должен быть заголовок Content-Type.
Более подробно - вот, например, серверный код из примера выше.

// уникальный разделитель, можете использовать конкат нескльких uniqid() и т.п.
$delimiter = "boundary";

header('Content-type: multipart/x-mixed-replace;boundary="'.$delimiter.'"');

// полностью отрубаем буферизацию
while (@ob_end_flush()) {}

$header = "Content-type: text/html\r\n\r\n"; // перед каждой частью

$boundary = "--{$delimiter}\n"; // между частями

$footer = "--{$delimiter}--\n"; // в конце

$max = 10;
 
for ($i = 1; $i <= $max; $i++){	
	echo $header; # Заголовок перед каждой частью ответа	
	echo "$i\n"; # пишем сообщение	
	echo $boundary; # и выводим границу между сообщениями
	sleep(1);    
}

// завершающее сообщение
echo $header;
echo "-1";
echo $footer;
  • минимальная задержка и трафик
  • нет индикации загрузки
  • данные нельзя сжимать
  • Firefox(Gecko) only (?)

Замечательный способ, но работает только в движке Gecko, т.е Firefox и родственные браузеры.


Автор: Вадим (не зарегистрирован), дата: 28 ноября, 2008 - 22:36
#permalink

да уж, и зачем тогда это нужно если только Лиса поддерживает?


Автор: Илья Кантор, дата: 30 ноября, 2008 - 13:35
#permalink

Ну варианта 2

1) пишется приложение только под "Лису"
2) этот способ используется для лисы а для других браузеров - другой


Автор: Гость (не зарегистрирован), дата: 2 октября, 2009 - 02:08
#permalink

Chrome работает с ним


Автор: Илья Кантор, дата: 2 октября, 2009 - 21:50
#permalink

Мой тест работает только в Firefox... Может быть, у Chrome свое API ?


Автор: Гость (не зарегистрирован), дата: 24 декабря, 2009 - 18:52
#permalink

чел наверно не понял какой должен быть результат, результат то конечно есть, но не тот, что ожидается


Автор: Axdr, дата: 19 января, 2010 - 12:34
#permalink

Chrome поддерживает.
Только немного по-другому. Нужен еще один обработчик:

req.onprogress = function () {...}

Чтобы что-то увидеть в event.target.responseText, приходится послать клиенту не меньше килобайта с 4мя переводами строки.

echo str_repeat(str_repeat(' ', 256)."<br>", 4);

и еще надо добавить flush(); после каждой посылки.


Автор: AVReg (не зарегистрирован), дата: 1 апреля, 2010 - 22:43
#permalink

multipart/x-mixed-replace, на мой взгляд, единственно правильная comet-технология.
Приведу 3 аргумента:

1) всё давным давно описано в RFC.
2) куски можно передавать любого тип, размера и кодирования и, причём, вперемешку.
3) Content-Length избавляет от тупого ресурсоёмкого постоянного grep-а.

По моим наблюдениями, Опера и Хром начинают поддерживать multipart/x-mixed-replace, однако пока через пень-колоду.
Например, Опере пофиг Content-Length очередного куска,
она будет ждать boundary след. блока прежде чем начать обработку.

Всё было бы хорошо, но "браузер для овощей" упорно не хочет поддерживать x-mixed-replace.


Автор: Arech (не зарегистрирован), дата: 2 июля, 2011 - 11:16
#permalink

Всё бы хорошо с этим методом, но есть одна проблема, которую мне не удалось решить (по видимому это баг в фаерфоксе), которая снижает до нуля всю ценность метода. Суть в том, что после прихода первого пакета с данными, перестают работать обработчики ошибок соединения; они просто ни на что не реагируют, ни error, ни abort, ни onreadystatechange. Можно соединение порвать любым способом, но никаких событий в JS это не сгенерирует и таким образом реализовать логику обработки ошибок невозможно. А без обработки ошибок, возможности вовремя и быстро перепослать запрос, ценности в методе нет никакой...

Альтернативой является эмуляция EventSource, причём не абы какая (тыщи их), а построенная на POST / readyState==3, как https://github.com/Yaffle/polyfills/blob/master/eventsource.js. Там по ловятся те ошибки, которые проскакивают в этом методе.


Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
4 + 6 =
Введите результат. Например, для 1+3, введите 4.
 
Текущий раздел
Поиск по сайту
Реклама
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Последние комментарии
Последние темы на форуме
Forum