Revista Informática

Los navegadores y los búferes de salida

Publicado el 13 agosto 2010 por Displaynone

Hace unos días me vi en la necesidad de implementar en una web un proceso que puede tardar uno o dos minutos dependiendo de varios factores. El problema reside en que es un proceso que inicia el usuario y el usuario normalmente es impaciente, por lo que es normal encontrarse con que se pulsa F5 (o un montón de teclas al azar) o se cierra el navegador pensando que la aplicación se ha colgado. Por eso pensé en implementar una barra de progreso con JavaScript (utilizando la estupenda librería jQuery UI) en la que el progreso fuera real y fiable. El proceso lento iría mostrando poco a poco el progreso y mediante JavaScript se interpreta y modifica la barra.

Para obtener progresivamente el contenido de una petición realizada con JavaScript hay varias técnicas y tecnologías (HTTP Streaming, Server-Sent Events…) pero opté por algo mucho más sencillo: cargar el script lento en un <iframe> que en teoría debería funcionar para todos los navegadores. Basta hacer que el proceso vaya mostrando poco a poco bloques de información procesable por JavaScript (JSON, por ejemplo) y con un setTimeout() se comprueba el contenido del <iframe>, obteniendo el último bloque. En mi caso, cada bloque contiene el porcentaje y un mensaje para que se vaya mostrando en la barra de progreso. El código PHP podría ser:

view source print?

1 echo json_encode(array('percent'=>10,'text'=>'Iniciando el proceso...')); flush();

2 echo json_encode(array('percent'=>50,'text'=>'Proceso a la mitad...')); flush();

3 echo json_encode(array('percent'=>100,'text'=>'Proceso finalizado...')); flush();

El problema es sólo Mozilla mostraba el contenido tras cada flush() mientras que los navegadores basados en Webkit así como Opera e Internet Explorer no mostraban nada hasta que terminaba el proceso. Tras mucho curiosear dí con la solución gracias a este comentario en PHP.net que explica que (exceptuando a los navegadores con motor Gecko) los navegadores no mostrarán nada hasta recibir una etiqueta HTML. Por eso, tras cada echo es necesario añadir una etiqueta como <br> que fuerce al navegador a mostrar el contenido. Además, algunas versiones de Internet Explorer no mostrarán nada hasta haber recibido al menos 256 bytes y Firefox no mostrará las líneas con menos de 8 bytes. Así que con las correcciones, el código PHP anterior quedaría así:

view source print?

1 echo str_repeat(' ', 256) . '<br>'; flush();

2 echo json_encode(array('percent'=>10,'text'=>'Iniciando el proceso...')) . '<br>'; flush();

3 echo json_encode(array('percent'=>50,'text'=>'Proceso a la mitad...')) . '<br>'; flush();

4 echo json_encode(array('percent'=>100,'text'=>'Proceso finalizado...')) . '<br>'; flush();

Es importante tener en cuenta que Internet Explorer procesa las etiquetas en mayúsculas, por lo que al acceder mediante JavaScript al contenido del marco oculto habrá que tenerlo en cuenta. En mi caso, al utilizar <br> como separador he de realizar la separación de los bloques mediante una expresión regular con el modificador /i.

También hay que destacar que hay ciertas cosas que pueden provocar que todo esto no funcione, como los módulos de compresión de los servidores web o ciertos módulos de seguridad como mod_security. En estos casos no se enviará nada al navegador hasta haberse completado la petición, por lo que no será posible obtener el contenido del marco oculto hasta que el servidor envíe todo de golpe.

Post original


Volver a la Portada de Logo Paperblog