Cách nhận tiến trình từ XMLHttpRequest


133

Có thể lấy tiến trình của XMLHttpRequest (byte được tải lên, byte được tải xuống) không?

Điều này sẽ hữu ích để hiển thị thanh tiến trình khi người dùng đang tải lên một tệp lớn. API tiêu chuẩn dường như không hỗ trợ nó, nhưng có lẽ có một số tiện ích mở rộng không chuẩn trong bất kỳ trình duyệt nào ngoài đó? Rốt cuộc, nó có vẻ như là một tính năng khá rõ ràng, vì khách hàng biết có bao nhiêu byte được tải lên / tải xuống.

lưu ý: Tôi biết về giải pháp thay thế "thăm dò máy chủ để tiến bộ" (đó là những gì tôi đang làm ngay bây giờ). vấn đề chính với điều này (ngoài mã phía máy chủ phức tạp) là thông thường, trong khi tải lên một tệp lớn, kết nối của người dùng hoàn toàn bị ngắt, bởi vì hầu hết các ISP cung cấp ngược dòng kém. Vì vậy, thực hiện các yêu cầu bổ sung không đáp ứng như tôi mong đợi. Tôi đã hy vọng sẽ có một cách (có thể là không chuẩn) để có được thông tin này, mà trình duyệt có mọi lúc.

Câu trả lời:


137

Đối với các byte được tải lên, nó khá dễ dàng. Chỉ cần theo dõi xhr.upload.onprogresssự kiện. Trình duyệt biết kích thước của các tệp mà nó phải tải lên và kích thước của dữ liệu được tải lên, vì vậy nó có thể cung cấp thông tin tiến trình.

Đối với các byte được tải xuống (khi nhận thông tin xhr.responseText), sẽ khó khăn hơn một chút, vì trình duyệt không biết có bao nhiêu byte sẽ được gửi trong yêu cầu máy chủ. Điều duy nhất mà trình duyệt biết trong trường hợp này là kích thước của byte mà nó đang nhận.

Có một giải pháp cho vấn đề này, nó đủ để đặt Content-Lengthtiêu đề trên tập lệnh máy chủ, để có được tổng kích thước của byte mà trình duyệt sẽ nhận được.

Để biết thêm, hãy truy cập https://developer.mozilla.org/en/Using_XMLHttpRequest .

Ví dụ: Tập lệnh máy chủ của tôi đọc tệp zip (mất 5 giây):

$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;

Bây giờ tôi có thể theo dõi quá trình tải xuống tập lệnh máy chủ, vì tôi biết tổng chiều dài của nó:

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
Đáng chú ý, "Độ dài nội dung" không phải là độ dài ước tính, nó phải có độ dài chính xác, quá ngắn và trình duyệt sẽ cắt tải xuống, quá dài và trình duyệt sẽ cho rằng quá trình tải xuống không hoàn thành.
Chris Chilvers

@ChrisChilvers Điều đó có nghĩa là một tệp PHP có thể không được tính toán chính xác, phải không?
Hydroper

1
@nicematt trong ví dụ đó sẽ ổn vì nó xuất phát từ một tệp, nhưng nếu bạn phát trực tiếp mã zip từ bộ nhớ, bạn không thể tạo ra độ dài nội dung hoặc ước tính nó.
Chris Chilvers

3
@TheProHands Khi bạn truy cập trang .php, máy chủ sẽ chạy tệp PHP và gửi cho bạn đầu ra của nó . Máy chủ nên gửi độ dài của đầu ra, không phải tệp .php.
leewz

Ai đó có thể vui lòng giải thích dòng "$ ('# tiến trình'). Thanh tiến trình (" tùy chọn "," giá trị ", phần trăm hoàn thành);" có nghĩa? Đây có phải là một plugin cụ thể không? Cái này hoạt động ra sao? Hãy làm cho câu trả lời dễ hiểu cho người dùng mới.
Zac


8

Một trong những cách tiếp cận hứa hẹn nhất dường như là mở một kênh liên lạc thứ hai trở lại máy chủ để hỏi nó rằng việc chuyển tiền đã được hoàn thành bao nhiêu.


24
Tôi nghĩ rằng liên kết có thể đã chết
Ronnie Royston


5

Đối với tổng số đã tải lên, dường như không có cách nào để xử lý việc đó, nhưng có một cái gì đó tương tự với những gì bạn muốn tải xuống. Khi readyState là 3, bạn có thể định kỳ truy vấn answerText để tải tất cả nội dung được tải xuống dưới dạng Chuỗi (điều này không hoạt động trong IE), cho đến khi tất cả nội dung có sẵn tại thời điểm nó sẽ chuyển sang readyState 4. Tổng cộng byte được tải xuống tại bất kỳ thời điểm nào sẽ bằng tổng số byte trong chuỗi được lưu trữ trong answerText.

Đối với cách tiếp cận toàn bộ hoặc không có gì cho câu hỏi tải lên, vì bạn phải truyền một chuỗi để tải lên (và có thể xác định tổng số byte đó), tổng số byte được gửi cho readyState 0 và 1 sẽ là 0 và tổng số cho readyState 2 sẽ là tổng số byte trong chuỗi bạn đã truyền. Tổng số byte cả được gửi và nhận trong readyState 3 và 4 sẽ là tổng số byte trong chuỗi gốc cộng với tổng số byte trong responsText.


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>

Kết quả


2

Nếu bạn có quyền truy cập vào cài đặt apache và tin tưởng mã của bên thứ ba, bạn có thể sử dụng mô-đun tiến trình tải lên apache (nếu bạn sử dụng apache; cũng có mô-đun tiến trình tải lên nginx ).

Mặt khác, bạn phải viết một tập lệnh mà bạn có thể thoát ra khỏi băng tần để yêu cầu trạng thái của tệp (ví dụ: kiểm tra kích thước tệp của tệp tmp).

Có một số công việc đang diễn ra trong firefox 3 Tôi tin rằng sẽ thêm hỗ trợ tiến trình tải lên cho trình duyệt, nhưng điều đó sẽ không được đưa vào tất cả các trình duyệt và được áp dụng rộng rãi trong một thời gian (đáng tiếc hơn).


-7

Cách duy nhất để làm điều đó với javascript thuần túy là thực hiện một số loại cơ chế bỏ phiếu. Bạn sẽ cần gửi các yêu cầu ajax theo các khoảng thời gian cố định (ví dụ mỗi 5 giây) để có được số byte mà máy chủ nhận được.

Một cách hiệu quả hơn sẽ là sử dụng đèn flash. Thành phần flex FileReference gửi định kỳ một sự kiện 'tiến trình' chứa số byte đã được tải lên. Nếu bạn cần gắn bó với javascript, các cầu nối có sẵn giữa Actioncript và javascript. Tin tốt là công việc này đã được thực hiện cho bạn :)

tải trọng

Thư viện này cho phép đăng ký một trình xử lý javascript về sự kiện tiến trình flash.

Giải pháp này có lợi thế lớn là không yêu cầu tài nguyên aditionnal ở phía máy chủ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.