Як отримати прогрес від 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(); 
}

29
Варто зауважити, що "Довжина вмісту" не є приблизною довжиною, вона повинна бути точної довжини, занадто короткою, і браузер перерве завантаження занадто довго, і браузер припустить, що завантаження не вдалося завершити.
Кріс Чілверс

@ChrisChilvers Це означає, що файл PHP може бути неправильно обчислений, правда?
Гідропер

1
@nicematt у цьому прикладі було б добре, оскільки він походить з файлу, але якби ви передавали поштовий індекс прямо з пам'яті, ви не могли просто скласти довжину вмісту або оцінити його.
Кріс Чілверс

3
@TheProHands Коли ви відвідуєте сторінку .php, сервер запускає файл PHP і надсилає вам його вихід . Сервер повинен надсилати довжину виводу, а не .php-файл.
leewz

Чи можете мені хтось пояснити, що це за рядок "$ ('# progressbar')? засоби? Це виклик певного плагіна? Як це працює? Будь ласка, зробіть відповідь зрозумілою для нових користувачів.
Зак


8

Одним з найбільш перспективних підходів, здається, є відкриття другого каналу зв'язку назад до сервера, щоб запитати, яка частина передачі завершена.


24
Я думаю, що посилання може бути мертвою
Ронні Ройстон


5

Щодо загальної кількості завантажених, схоже, немає способу впоратися з цим, але є щось подібне до того, що ви хочете завантажити. Після того, як ReadyState дорівнює 3, ви можете періодично запитувати responseText, щоб отримати весь вміст, завантажений настільки, як String (це не працює в IE), до тих пір, поки не буде доступно все, в який момент він перейде до ReadyState 4. Загальний байтів, завантажених у будь-який момент часу, буде дорівнює загальній кількості байтів у рядку, збереженому у responseText.

Для підходу до питання щодо завантаження або нічого, оскільки вам потрібно пройти рядок для завантаження (і можна визначити загальну кількість байтів для цього), загальний байт, надісланий для ReadyState 0 і 1, буде 0, а загальний для ReadyState 2 буде загальним байтом у рядку, який ви передали. Загальна кількість байтів, надісланих та отриманих у ReadyState 3 та 4, буде сумою байтів у вихідному рядку плюс загальна кількість байтів у responseText.


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; також є модуль ходу завантаження nginx ).

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

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


-7

Єдиний спосіб зробити це за допомогою чистого javascript - це реалізувати якийсь механізм опитування. Вам потрібно буде надсилати запити ajax через фіксований інтервал (наприклад, кожні 5 секунд), щоб отримати кількість байтів, отриманих сервером.

Більш ефективним способом було б використання спалаху. Flex компонент FileReference періодично відправляє подію "прогрес", що містить кількість завантажених байтів. Якщо вам потрібно дотримуватися javascript, мости доступні між сценарієм action та javascript. Хороша новина в тому, що ця робота вже зроблена для вас :)

swfupload

Ця бібліотека дозволяє зареєструвати обробник javascript на події прогресу флеш.

Таке рішення має перевагу в тому, що він не потребує додаткових ресурсів на стороні сервера.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.