2008-09-16 27 views
115

¿Es posible obtener el progreso de un XMLHttpRequest (bytes cargados, bytes descargados)?Cómo obtener progreso desde XMLHttpRequest

Esto sería útil para mostrar una barra de progreso cuando el usuario carga un archivo grande. La API estándar no parece ser compatible, pero tal vez hay alguna extensión no estándar en cualquiera de los navegadores que hay. Después de todo, parece una característica bastante obvia, ya que el cliente sabe cuántos bytes se cargaron/descargaron.

Nota: Conozco la alternativa "sondear al servidor para el progreso" (es lo que estoy haciendo en este momento). El principal problema con esto (aparte del complicado código del lado del servidor) es que, típicamente, mientras se carga un archivo grande, la conexión del usuario es completamente regada, porque la mayoría de los ISP ofrecen un flujo ascendente pobre. Así que hacer solicitudes adicionales no es tan receptivo como esperaba. Esperaba que hubiera una manera (quizás no estándar) de obtener esta información, que el navegador tiene en todo momento.

Respuesta

10

Hay una buena discusión del indicador de progreso para el patrón AJAX aquí:

http://ajaxpatterns.org/Progress_Indicator

Uno de los enfoques más prometedores parece estar abriendo un segundo canal de comunicación de vuelta al servidor para pedir que la cantidad de la transferencia ha sido completada

+7

creo que el enlace podría estar muerto –

2

Si tiene acceso a su instalación de Apache y confía en un código de terceros, puede usar el apache upload progress module (si utiliza apache, también hay un nginx upload progress module).

De lo contrario, tendría que escribir una secuencia de comandos que puede golpear fuera de banda para solicitar el estado del archivo (comprobar el tamaño del archivo tmp, por ejemplo).

Hay algo de trabajo pasando en firefox 3 Creo que para agregar soporte de progreso de carga para el navegador, pero que no va a entrar en todos los navegadores y ser ampliamente adoptado por un tiempo (más pena).

4

Para el total de carga no parece haber una manera de manejar eso, pero hay algo similar a lo que desea descargar. Una vez que readyState es 3, puede consultar responseText periódicamente para obtener todo el contenido descargado hasta ahora como String (esto no funciona en IE), hasta que todo esté disponible y en ese punto pasará a readyState 4. El total los bytes descargados en cualquier momento dado serán iguales al total de bytes en la cadena almacenada en responseText.

Para un enfoque de todo o nada a la cuestión de carga, ya que tiene que pasar una cadena de carga (y es posible determinar el total de bytes de que) el total de bytes enviados por readyState 0 y 1 será 0, y el total para readyState 2 será el total de bytes en la cadena que pasó. El total de bytes enviados y recibidos en readyState 3 y 4 será la suma de los bytes en la cadena original más el total de bytes en responseText.

-6

La única forma de hacerlo con javascript puro es implementar algún tipo de mecanismo de sondeo. Tendrá que enviar solicitudes ajax a intervalos fijos (cada 5 segundos, por ejemplo) para obtener la cantidad de bytes recibidos por el servidor.

Una forma más eficiente sería usar flash. El componente de flex FileReference despacha periódicamente un evento de 'progreso' que contiene el número de bytes ya cargados. Si necesita seguir con Javascript, hay puentes disponibles entre actionscript y javascript. La buena noticia es que este trabajo se ha hecho ya para ti :)

swfupload

Esta biblioteca permite registrar un manejador de javascript en el evento progress flash.

Esta solución tiene la gran ventaja de no requerir recursos adicionales en el lado del servidor.

122

Para los bytes cargados es bastante fácil. Solo monitoree el evento xhr.upload.onprogress. El navegador conoce el tamaño de los archivos que debe cargar y el tamaño de los datos cargados, de modo que puede proporcionar la información de progreso.

Para los bytes descargados (cuando se obtiene la información con xhr.responseText), es un poco más difícil, porque el navegador no sabe cuántos bytes se enviarán en la solicitud del servidor. Lo único que sabe el navegador en este caso es el tamaño de los bytes que está recibiendo.

Existe una solución para esto, es suficiente establecer un encabezado Content-Length en el script del servidor, para obtener el tamaño total de los bytes que el navegador va a recibir.

Para obtener más información, ingrese a https://developer.mozilla.org/en/Using_XMLHttpRequest.

Ejemplo: Mi script de servidor lee un archivo zip (se tarda 5 segundos):

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

Ahora puedo controlar el proceso de descarga de la secuencia de comandos del servidor, porque sé que es longitud total:

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(); 
} 
+24

A tener en cuenta, "Content-Length" no es una longitud estimada, que tiene que ser la exacta de longitud, demasiado corto y el navegador cortará la descarga, demasiado tiempo y el navegador supondrá que la descarga no se completó. –

+1

sí correcto, estimado es incorrecto, solo una confusión – albanx

+0

@ChrisChilvers Eso significa que un archivo PHP puede no ser calculado correctamente, ¿no? – Hydro

2

<!DOCTYPE html> 
 
<html> 
 
<body> 
 
<p id="demo">result</p> 
 
<button type="button" onclick="get_post_ajax();">Change Content</button> 
 
<script type="text/javascript"> 
 
\t function update_progress(e) 
 
\t { 
 
\t if (e.lengthComputable) 
 
\t { 
 
\t  var percentage = Math.round((e.loaded/e.total)*100); 
 
\t  console.log("percent " + percentage + '%'); 
 
\t } 
 
\t else 
 
\t { 
 
\t \t console.log("Unable to compute progress information since the total size is unknown"); 
 
\t } 
 
\t } 
 
\t function transfer_complete(e){console.log("The transfer is complete.");} 
 
\t function transfer_failed(e){console.log("An error occurred while transferring the file.");} 
 
\t function transfer_canceled(e){console.log("The transfer has been canceled by the user.");} 
 
\t function get_post_ajax() 
 
\t { 
 
\t \t var xhttp; 
 
\t \t if (window.XMLHttpRequest){xhttp = new XMLHttpRequest();}//code for modern browsers} 
 
\t \t else{xhttp = new ActiveXObject("Microsoft.XMLHTTP");}// code for IE6, IE5 \t \t 
 
\t \t xhttp.onprogress = update_progress; 
 
\t \t xhttp.addEventListener("load", transfer_complete, false); 
 
\t \t xhttp.addEventListener("error", transfer_failed, false); 
 
\t \t xhttp.addEventListener("abort", transfer_canceled, false); \t \t 
 
\t \t xhttp.onreadystatechange = function() 
 
\t \t { 
 
\t  \t if (xhttp.readyState == 4 && xhttp.status == 200) 
 
\t  \t { 
 
\t  \t \t document.getElementById("demo").innerHTML = xhttp.responseText; 
 
\t  \t } 
 
\t \t }; 
 
\t xhttp.open("GET", "http://it-tu.com/ajax_test.php", true); 
 
\t xhttp.send(); 
 
\t } 
 
</script> 
 
</body> 
 
</html>

Result

Cuestiones relacionadas