วิธีรับความคืบหน้าจาก XMLHttpRequest


134

เป็นไปได้หรือไม่ที่จะรับความคืบหน้าของ XMLHttpRequest (ไบต์ที่อัปโหลดดาวน์โหลดไบต์)

นี่จะเป็นประโยชน์ในการแสดงแถบความคืบหน้าเมื่อผู้ใช้อัปโหลดไฟล์ขนาดใหญ่ ดูเหมือนว่า API มาตรฐานจะไม่รองรับ แต่อาจมีส่วนขยายที่ไม่ได้มาตรฐานในเบราว์เซอร์ใด ๆ ดูเหมือนว่าจะมีคุณลักษณะที่ชัดเจนเนื่องจากลูกค้าทราบว่ามีการอัปโหลด / ดาวน์โหลดจำนวนไบต์

หมายเหตุ: ฉันทราบถึงทางเลือก "สำรวจความคืบหน้าของเซิร์ฟเวอร์" (ตอนนี้ฉันกำลังทำอะไรอยู่) ปัญหาหลักของสิ่งนี้ (นอกเหนือจากรหัสฝั่งเซิร์ฟเวอร์ที่ซับซ้อน) คือโดยทั่วไปแล้วในขณะที่อัปโหลดไฟล์ขนาดใหญ่การเชื่อมต่อของผู้ใช้จะถูกปิดลงอย่างสมบูรณ์เนื่องจาก ISP ส่วนใหญ่ให้บริการอัปสตรีมที่ไม่ดี ดังนั้นการร้องขอเพิ่มเติมจึงไม่ตอบสนองอย่างที่หวัง ฉันหวังว่าจะมีวิธี (อาจไม่ได้มาตรฐาน) ในการรับข้อมูลนี้ซึ่งเบราว์เซอร์มีอยู่ตลอดเวลา

คำตอบ:


138

สำหรับไบต์ที่อัพโหลดนั้นค่อนข้างง่าย เพียงแค่เฝ้าติดตาม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
ที่น่าสังเกตว่า "ความยาวเนื้อหา" ไม่ใช่ความยาวโดยประมาณ แต่ต้องมีความยาวที่แน่นอนสั้นเกินไปและเบราว์เซอร์จะตัดการดาวน์โหลดยาวเกินไปและเบราว์เซอร์จะถือว่าการดาวน์โหลดล้มเหลว
Chris Chilvers

@ChrisChilvers นั่นหมายความว่าไฟล์ PHP อาจคำนวณไม่ถูกต้องใช่ไหม?
Hydroper

1
@nicematt ในตัวอย่างนั้นมันจะดีเพราะมาจากไฟล์ แต่ถ้าคุณสตรีมไฟล์ zip โดยตรงจากหน่วยความจำคุณไม่สามารถสร้างความยาวของเนื้อหาหรือประมาณได้
Chris Chilvers

3
@TheProHands เมื่อคุณไปที่หน้า. php เซิร์ฟเวอร์จะเรียกใช้ไฟล์ PHP และส่งเอาต์พุตให้คุณ เซิร์ฟเวอร์ควรส่งความยาวของเอาต์พุตไม่ใช่ไฟล์. php
leewz

ใครช่วยอธิบายหน่อยว่าบรรทัด "$ ('# progressbar'). progressbar (" option "," value ", percentComplete);" คืออะไร หมายความว่า? นี่เป็นการเรียกใช้ปลั๊กอินเฉพาะหรือไม่? วิธีนี้ทำงานอย่างไร? โปรดทำให้ผู้ใช้ใหม่เข้าใจคำตอบ
Zac


8

หนึ่งในแนวทางที่มีแนวโน้มมากที่สุดดูเหมือนจะเป็นการเปิดช่องทางการสื่อสารที่สองกลับไปที่เซิร์ฟเวอร์เพื่อถามว่าการถ่ายโอนเสร็จสิ้นไปแล้วเท่าใด


24
ฉันคิดว่าลิงค์อาจจะตายไปแล้ว
Ronnie Royston


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 ได้ (หากคุณใช้ apache นอกจากนี้ยังมีโมดูลความคืบหน้าการอัปโหลด nginx )

มิฉะนั้นคุณจะต้องเขียนสคริปต์ที่คุณสามารถตีออกจากวงดนตรีเพื่อขอสถานะของไฟล์ (เช่นตรวจสอบขนาดไฟล์ของไฟล์ tmp)

มีงานบางอย่างเกิดขึ้นใน firefox 3 ฉันเชื่อว่าจะเพิ่มการสนับสนุนความคืบหน้าในการอัปโหลดไปยังเบราว์เซอร์ แต่จะไม่เข้าสู่เบราว์เซอร์ทั้งหมดและได้รับการยอมรับอย่างกว้างขวางในขณะนี้


-7

วิธีเดียวที่จะทำได้ด้วยจาวาสคริปต์ล้วน ๆ คือใช้กลไกการสำรวจความคิดเห็นบางประเภท คุณจะต้องส่งคำขอ ajax ตามช่วงเวลาที่กำหนด (เช่นทุกๆ 5 วินาที) เพื่อรับจำนวนไบต์ที่เซิร์ฟเวอร์ได้รับ

วิธีที่มีประสิทธิภาพมากขึ้นคือการใช้แฟลช FileReferenceคอมโพเนนต์แบบยืดหยุ่นจะส่งเหตุการณ์ 'ความคืบหน้า' เป็นระยะโดยมีจำนวนไบต์ที่อัปโหลดแล้ว หากคุณต้องการใช้จาวาสคริปต์บริดจ์จะมีให้ใช้ระหว่าง actionscript และ javascript ข่าวดีก็คืองานนี้ได้ทำสำเร็จแล้วสำหรับคุณ :)

swfupload

ไลบรารีนี้อนุญาตให้ลงทะเบียนตัวจัดการจาวาสคริปต์ในเหตุการณ์ความคืบหน้าของแฟลช

โซลูชันนี้มีข้อได้เปรียบที่ไม่ต้องการทรัพยากร aditionnal ทางฝั่งเซิร์ฟเวอร์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.