Как получить прогресс от XMLHttpRequest

133

Можно ли получить ход выполнения запроса XMLHttpRequest (загружено байт, загружено байт)?

Это было бы полезно, чтобы показать индикатор выполнения, когда пользователь загружает большой файл. Стандартный API, похоже, не поддерживает его, но, может быть, есть какое-то нестандартное расширение в любом из браузеров? В конце концов, это довольно очевидная возможность, поскольку клиент знает, сколько байт было загружено / загружено.

примечание: я знаю об альтернативе «опросить сервер о прогрессе» (это то, что я делаю сейчас). основная проблема с этим (кроме сложного кода на стороне сервера) заключается в том, что, как правило, при загрузке большого файла подключение пользователя полностью скрывается, поскольку большинство интернет-провайдеров предлагают плохой восходящий поток. Таким образом, дополнительные запросы не так оперативны, как я надеялся. Я надеялся, что найдется способ (возможно, нестандартный) получить эту информацию, которую браузер всегда имеет.

Пит
источник

Ответы:

137

Для загруженных байтов это довольно просто. Просто следите за xhr.upload.onprogressсобытием. Браузер знает размер файлов, которые он должен загрузить, и размер загружаемых данных, поэтому он может предоставить информацию о ходе выполнения.

Для загруженных байтов (при получении информации xhr.responseText) это немного сложнее, потому что браузер не знает, сколько байтов будет отправлено в запросе сервера. Единственное, что знает браузер в этом случае, это размер байтов, которые он получает.

Для этого есть решение: достаточно установить Content-Lengthзаголовок на серверном скрипте, чтобы получить общий размер байтов, которые собирается получить браузер.

Для получения дополнительной информации перейдите на https://developer.mozilla.org/en/Using_XMLHttpRequest .

Пример: мой серверный скрипт читает zip-файл (это занимает 5 секунд):

$filesize=filesize('test.zip');

header("Content-Length: " . $filesize); // set header length
// if the headers is not set then the evt.loaded will be 0
readfile('test.zip');
exit 0;

Теперь я могу контролировать процесс загрузки серверного скрипта, потому что я знаю, что это общая длина:

function updateProgress(evt) 
{
   if (evt.lengthComputable) 
   {  // evt.loaded the bytes the browser received
      // evt.total the total bytes set by the header
      // jQuery UI progress bar to show the progress on screen
     var percentComplete = (evt.loaded / evt.total) * 100;  
     $('#progressbar').progressbar( "option", "value", percentComplete );
   } 
}   
function sendreq(evt) 
{  
    var req = new XMLHttpRequest(); 
    $('#progressbar').progressbar();    
    req.onprogress = updateProgress;
    req.open('GET', 'test.php', true);  
    req.onreadystatechange = function (aEvt) {  
        if (req.readyState == 4) 
        {  
             //run any callback here
        }  
    };  
    req.send(); 
}
albanx
источник
29
Стоит отметить, что «Content-Length» - это не примерная длина, она должна быть точной длины, слишком короткая, и браузер будет отключать загрузку, слишком долго, и браузер будет считать, что загрузка не была завершена.
Крис Чилверс
@ChrisChilvers Это означает, что файл PHP может быть рассчитан неправильно, верно?
Hydroper,
1
@nicematt в этом примере было бы хорошо, поскольку он исходит из файла, но если бы вы передавали zip-архив прямо из памяти, вы не могли бы просто определить длину содержимого или оценить ее.
Крис Чилверс
3
@TheProHands Когда вы посещаете страницу .php, сервер запускает файл PHP и отправляет вам свои выходные данные . Сервер должен посылать длину вывода, а не файл .php.
Leewz
Может кто-нибудь объяснить, что такое строка "$ ('# progressbar'). Progressbar (" option "," value ", percentComplete);" средства? Это вызывает определенный плагин? Как это работает? Пожалуйста, сделайте ответ понятным для новых пользователей.
Зак
8

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

Шон МакМейнс
источник
24
Я думаю, что ссылка может быть мертвой
Ронни Ройстон
5

Для всего загруженного, похоже, нет способа справиться с этим, но есть нечто похожее на то, что вы хотите загрузить. Как только значение readyState равно 3, вы можете периодически запрашивать responseText, чтобы получить весь контент, загруженный в виде String (это не работает в IE), до тех пор, пока все это не станет доступным, и в этот момент он перейдет в состояние readyState 4. Итого байт, загруженные в любой момент времени, будут равны общему количеству байтов в строке, хранящейся в responseText.

Для подхода «все или ничего» к вопросу о загрузке, поскольку вам нужно передать строку для загрузки (и можно определить общее количество байтов этого), общее количество байтов, отправленных для readyState 0 и 1, будет равно 0, а общее количество для readyState 2 будет общим количеством байтов в строке, которую вы передали. Общее количество байтов, отправленных и полученных в readyState 3 и 4, будет суммой байтов в исходной строке плюс общее количество байтов в responseText.

Orclev
источник
3

<!DOCTYPE html>
<html>
<body>
<p id="demo">result</p>
<button type="button" onclick="get_post_ajax();">Change Content</button>
<script type="text/javascript">
	function update_progress(e)
	{
	  if (e.lengthComputable)
	  {
	    var percentage = Math.round((e.loaded/e.total)*100);
	    console.log("percent " + percentage + '%' );
	  }
	  else 
	  {
	  	console.log("Unable to compute progress information since the total size is unknown");
	  }
	}
	function transfer_complete(e){console.log("The transfer is complete.");}
	function transfer_failed(e){console.log("An error occurred while transferring the file.");}
	function transfer_canceled(e){console.log("The transfer has been canceled by the user.");}
	function get_post_ajax()
	{
	  	var xhttp;
	  	if (window.XMLHttpRequest){xhttp = new XMLHttpRequest();}//code for modern browsers} 
	 	else{xhttp = new ActiveXObject("Microsoft.XMLHTTP");}// code for IE6, IE5	  	
	  	xhttp.onprogress = update_progress;
		xhttp.addEventListener("load", transfer_complete, false);
		xhttp.addEventListener("error", transfer_failed, false);
		xhttp.addEventListener("abort", transfer_canceled, false);	  	
	  	xhttp.onreadystatechange = function()
	  	{
	    	if (xhttp.readyState == 4 && xhttp.status == 200)
	    	{
	      		document.getElementById("demo").innerHTML = xhttp.responseText;
	    	}
	  	};
	  xhttp.open("GET", "http://it-tu.com/ajax_test.php", true);
	  xhttp.send();
	}
</script>
</body>
</html>

результат

Форумы любовника
источник
2

Если у вас есть доступ к вашей установке apache и вы доверяете стороннему коду, вы можете использовать модуль прогресса загрузки apache (если вы используете apache; есть также модуль прогресса загрузки nginx ).

В противном случае вам придется написать сценарий, который вы можете нажать вне диапазона, чтобы запросить статус файла (например, проверяя размер файла tmp).

В Firefox 3 ведется некоторая работа по добавлению поддержки процесса загрузки в браузер, но это не войдет во все браузеры и не получит широкого распространения в течение некоторого времени (жаль больше).

геологический период
источник
-7

Единственный способ сделать это с помощью чистого JavaScript - это реализовать какой-то механизм опроса. Вам нужно будет отправлять запросы AJAX через фиксированные интервалы (например, каждые 5 секунд), чтобы получить количество байтов, полученных сервером.

Более эффективным способом было бы использовать вспышку. Гибкий компонент FileReference периодически отправляет событие «progress», содержащее количество уже загруженных байтов. Если вам нужно придерживаться Javascript, мосты доступны между ActionScript и Javascript. Хорошей новостью является то, что эта работа уже сделана для вас :)

SWFUpload

Эта библиотека позволяет зарегистрировать обработчик javascript в событии прогресса флэш-памяти.

Преимущество этого решения в том, что он не требует дополнительных ресурсов на стороне сервера.

Александр виктор
источник