Comment obtenir la progression de XMLHttpRequest

133

Est-il possible d'obtenir la progression d'une XMLHttpRequest (octets téléchargés, octets téléchargés)?

Cela serait utile pour afficher une barre de progression lorsque l'utilisateur télécharge un fichier volumineux. L'API standard ne semble pas le prendre en charge, mais peut-être y a-t-il une extension non standard dans l'un des navigateurs? Cela semble être une fonctionnalité assez évidente après tout, car le client sait combien d'octets ont été chargés / téléchargés.

note: je connais l'alternative "interroger le serveur pour les progrès" (c'est ce que je fais en ce moment). le principal problème avec ceci (autre que le code compliqué côté serveur) est que généralement, lors du téléchargement d'un gros fichier, la connexion de l'utilisateur est complètement interrompue, car la plupart des FAI offrent une mauvaise qualité en amont. Donc, faire des demandes supplémentaires n'est pas aussi réactif que je l'espérais. J'espérais qu'il y aurait un moyen (peut-être non standard) d'obtenir ces informations, dont le navigateur dispose à tout moment.

Pete
la source

Réponses:

137

Pour les octets téléchargés, c'est assez simple. Surveillez simplement l' xhr.upload.onprogressévénement. Le navigateur connaît la taille des fichiers qu'il doit télécharger et la taille des données téléchargées, de sorte qu'il peut fournir les informations de progression.

Pour les octets téléchargés (lors de l'obtention des informations avec xhr.responseText), c'est un peu plus difficile, car le navigateur ne sait pas combien d'octets seront envoyés dans la requête du serveur. La seule chose que le navigateur sait dans ce cas est la taille des octets qu'il reçoit.

Il existe une solution pour cela, il suffit de définir un en- Content-Lengthtête sur le script serveur, afin d'obtenir la taille totale des octets que le navigateur va recevoir.

Pour en savoir plus, rendez-vous sur https://developer.mozilla.org/en/Using_XMLHttpRequest .

Exemple: mon script serveur lit un fichier zip (cela prend 5 secondes):

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

Maintenant, je peux surveiller le processus de téléchargement du script serveur, car je sais que sa longueur totale:

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
la source
29
Il convient de noter que "Content-Length" n'est pas une longueur estimée, il doit s'agir de la longueur exacte, trop courte et le navigateur coupera le téléchargement, trop long et le navigateur supposera que le téléchargement n'a pas abouti.
Chris Chilvers
@ChrisChilvers Cela signifie qu'un fichier PHP peut ne pas être correctement calculé, non?
Hydroper
1
@nicematt dans cet exemple, ce serait très bien car il provient d'un fichier, mais si vous diffusiez le zip directement à partir de la mémoire, vous ne pourriez pas simplement créer une longueur de contenu ou l'estimer.
Chris Chilvers
3
@TheProHands Lorsque vous visitez une page .php, le serveur exécute le fichier PHP et vous envoie sa sortie . Le serveur doit envoyer la longueur de la sortie, pas le fichier .php.
leewz
Quelqu'un peut-il expliquer ce que la ligne "$ ('# progressbar'). Progressbar (" option "," value ", percentComplete);" veux dire? Est-ce que cela appelle un plugin spécifique? Comment cela marche-t-il? Veuillez rendre la réponse compréhensible pour les nouveaux utilisateurs.
Zac
8

L'une des approches les plus prometteuses semble être d'ouvrir un deuxième canal de communication vers le serveur pour lui demander combien de transfert a été effectué.

Sean McMains
la source
24
Je pense que le lien pourrait être mort
Ronnie Royston
5

Pour le total téléchargé, il ne semble pas y avoir de moyen de gérer cela, mais il y a quelque chose de similaire à ce que vous voulez télécharger. Une fois que readyState est 3, vous pouvez périodiquement interroger responseText pour obtenir tout le contenu téléchargé dans la mesure où une chaîne (cela ne fonctionne pas dans IE), jusqu'à ce que tout soit disponible, auquel point il passera à readyState 4. Le total les octets téléchargés à un moment donné seront égaux au nombre total d'octets de la chaîne stockée dans responseText.

Pour une approche tout ou rien de la question de téléchargement, puisque vous devez passer une chaîne pour le téléchargement (et il est possible de déterminer le nombre total d'octets de cela), le total des octets envoyés pour readyState 0 et 1 sera 0, et le total pour readyState 2 sera le nombre total d'octets dans la chaîne que vous avez transmise. Le total des octets envoyés et reçus dans readyState 3 et 4 sera la somme des octets de la chaîne d'origine plus le total des octets dans responseText.

Orclev
la source
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>

Résultat

Amoureux des forums
la source
2

Si vous avez accès à votre installation apache et que vous faites confiance au code tiers, vous pouvez utiliser le module de progression de téléchargement apache (si vous utilisez apache; il existe également un module de progression de téléchargement nginx ).

Sinon, vous devrez écrire un script que vous pouvez frapper hors bande pour demander l'état du fichier (en vérifiant la taille du fichier tmp par exemple).

Il y a du travail en cours dans Firefox 3, je crois, pour ajouter la prise en charge de la progression du téléchargement au navigateur, mais cela ne va pas entrer dans tous les navigateurs et être largement adopté pendant un certain temps (c'est dommage).

Temps infini
la source
-7

La seule façon de faire cela avec du javascript pur est d'implémenter une sorte de mécanisme d'interrogation. Vous devrez envoyer des requêtes ajax à intervalles fixes (toutes les 5 secondes par exemple) pour obtenir le nombre d'octets reçus par le serveur.

Un moyen plus efficace serait d'utiliser le flash. Le composant flexible FileReference envoie périodiquement un événement 'progress' contenant le nombre d'octets déjà téléchargés. Si vous devez vous en tenir à javascript, des ponts sont disponibles entre actionscript et javascript. La bonne nouvelle est que ce travail a déjà été fait pour vous :)

swfupload

Cette bibliothèque permet d'enregistrer un gestionnaire javascript sur l'événement flash progress.

Cette solution a le gros avantage de ne pas nécessiter de ressources supplémentaires côté serveur.

Alexandre Victoor
la source