So erhalten Sie Fortschritte mit XMLHttpRequest

133

Ist es möglich, den Fortschritt einer XMLHttpRequest abzurufen (Bytes hochgeladen, Bytes heruntergeladen)?

Dies ist nützlich, um einen Fortschrittsbalken anzuzeigen, wenn der Benutzer eine große Datei hochlädt. Die Standard-API scheint dies nicht zu unterstützen, aber vielleicht gibt es in einem der Browser eine nicht standardmäßige Erweiterung? Es scheint doch eine ziemlich offensichtliche Funktion zu sein, da der Client weiß, wie viele Bytes hochgeladen / heruntergeladen wurden.

Hinweis: Mir ist die Alternative "Server auf Fortschritt abfragen" bekannt (genau das mache ich gerade). Das Hauptproblem dabei (abgesehen von dem komplizierten serverseitigen Code) besteht darin, dass beim Hochladen einer großen Datei die Verbindung des Benutzers normalerweise vollständig unterbrochen wird, da die meisten ISPs einen schlechten Upstream bieten. Zusätzliche Anfragen zu stellen ist also nicht so reaktionsschnell wie ich gehofft hatte. Ich hatte gehofft, dass es einen Weg geben würde (vielleicht nicht zum Standard), um diese Informationen zu erhalten, über die der Browser jederzeit verfügt.

Pete
quelle

Antworten:

137

Für die hochgeladenen Bytes ist das ganz einfach. Überwachen Sie einfach das xhr.upload.onprogressEreignis. Der Browser kennt die Größe der hochzuladenden Dateien und die Größe der hochgeladenen Daten, sodass er die Fortschrittsinformationen bereitstellen kann.

Für die heruntergeladenen Bytes (beim Abrufen der Informationen mit xhr.responseText) ist es etwas schwieriger, da der Browser nicht weiß, wie viele Bytes in der Serveranforderung gesendet werden. Das einzige, was der Browser in diesem Fall weiß, ist die Größe der empfangenen Bytes.

Es gibt eine Lösung für dieses, ist es ausreichend , einen zu setzen Content-LengthHeader auf dem Server - Skript, um die Gesamtgröße zu erhalten der Bytes der Browser empfangen wird.

Weitere Informationen finden Sie unter https://developer.mozilla.org/en/Using_XMLHttpRequest .

Beispiel: Mein Serverskript liest eine Zip-Datei (es dauert 5 Sekunden):

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

Jetzt kann ich den Downloadprozess des Serverskripts überwachen, da ich weiß, dass es insgesamt lang ist:

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
quelle
29
Es ist erwähnenswert, dass "Content-Length" keine geschätzte Länge ist. Es muss die exakte Länge sein, zu kurz, und der Browser unterbricht den Download, zu lang, und der Browser geht davon aus, dass der Download nicht abgeschlossen werden konnte.
Chris Chilvers
@ChrisChilvers Das bedeutet, dass eine PHP-Datei möglicherweise nicht korrekt berechnet wird, oder?
Hydroper
1
@nicematt in diesem Beispiel wäre es in Ordnung, da es aus einer Datei stammt, aber wenn Sie die Zip-Datei direkt aus dem Speicher streamen würden, könnten Sie nicht einfach eine Inhaltslänge erstellen oder diese schätzen.
Chris Chilvers
3
@TheProHands Wenn Sie eine .php-Seite besuchen, führt der Server die PHP-Datei aus und sendet Ihnen ihre Ausgabe . Der Server sollte die Länge der Ausgabe senden, nicht die .php-Datei.
Leez
Kann jemand bitte erklären, was die Zeile "$ ('# progressbar'). Progressbar (" Option "," Wert ", ProzentComplete);" meint? Ruft dies ein bestimmtes Plugin auf? Wie funktioniert das? Bitte machen Sie die Antwort für neue Benutzer verständlich.
Zac
8

Einer der vielversprechendsten Ansätze scheint darin zu bestehen, einen zweiten Kommunikationskanal zurück zum Server zu öffnen, um ihn zu fragen, wie viel der Übertragung abgeschlossen wurde.

Sean McMains
quelle
24
Ich denke, der Link könnte tot sein
Ronnie Royston
5

Für die insgesamt hochgeladene Version scheint es keine Möglichkeit zu geben, damit umzugehen, aber es gibt etwas Ähnliches wie das, was Sie zum Herunterladen wünschen. Sobald readyState 3 ist, können Sie responseText regelmäßig abfragen, um den gesamten Inhalt bis zu einem String herunterzuladen (dies funktioniert im IE nicht), bis alles verfügbar ist. Zu diesem Zeitpunkt wechselt er zu readyState 4. Die Summe Zu einem bestimmten Zeitpunkt heruntergeladene Bytes entsprechen der Gesamtzahl der Bytes in der in responseText gespeicherten Zeichenfolge.

Für einen Alles-oder-Nichts-Ansatz für die Upload-Frage sind die für readyState 0 und 1 gesendeten Gesamtbytes 0 und die Summe für readyState 0, da Sie eine Zeichenfolge zum Hochladen übergeben müssen (und es ist möglich, die Gesamtbytes davon zu bestimmen) 2 ist die Gesamtzahl der Bytes in der Zeichenfolge, die Sie übergeben haben. Die Gesamtzahl der in readyState 3 und 4 gesendeten und empfangenen Bytes ist die Summe der Bytes in der ursprünglichen Zeichenfolge plus der Gesamtzahl der Bytes in responseText.

Orclev
quelle
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>

Ergebnis

Forenliebhaber
quelle
2

Wenn Sie Zugriff auf Ihre Apache-Installation haben und Code von Drittanbietern vertrauen, können Sie das Fortschrittsmodul für den Apache-Upload verwenden (wenn Sie Apache verwenden, gibt es auch eine Fortschrittsmodul für den Nginx-Upload ).

Andernfalls müssten Sie ein Skript schreiben, das Sie außerhalb des Bandes aufrufen können, um den Status der Datei anzufordern (z. B. die Dateigröße der tmp-Datei überprüfen).

In Firefox 3 wird derzeit daran gearbeitet, dem Browser Unterstützung für den Upload-Fortschritt hinzuzufügen, aber das wird nicht in alle Browser gelangen und für eine Weile weit verbreitet sein (mehr schade).

Äon
quelle
-7

Die einzige Möglichkeit, dies mit reinem Javascript zu tun, besteht darin, eine Art Abfragemechanismus zu implementieren. Sie müssen Ajax-Anforderungen in festen Intervallen (z. B. alle 5 Sekunden) senden, um die Anzahl der vom Server empfangenen Bytes abzurufen.

Ein effizienterer Weg wäre die Verwendung von Flash. Die Flex-Komponente FileReference löst regelmäßig ein Fortschrittsereignis aus, das die Anzahl der bereits hochgeladenen Bytes enthält. Wenn Sie sich an Javascript halten müssen, stehen Brücken zwischen Actionscript und Javascript zur Verfügung. Die gute Nachricht ist, dass diese Arbeit bereits für Sie erledigt wurde :)

swfupload

Diese Bibliothek ermöglicht die Registrierung eines Javascript-Handlers für das Flash-Fortschrittsereignis.

Diese Lösung hat den Vorteil, dass auf der Serverseite keine zusätzlichen Ressourcen erforderlich sind.

Alexandre Victoor
quelle