Tengo una página que permite al usuario descargar un archivo generado dinámicamente. Se tarda mucho tiempo en generar, por lo que me gustaría mostrar un indicador de "espera". El problema es que no puedo descubrir cómo detectar cuándo el navegador ha recibido el archivo, así que puedo ocultar el indicador.
Estoy haciendo la solicitud en una forma oculta, que POSTs al servidor, y apunta a un iframe oculto por sus resultados. Esto es para que no reemplace toda la ventana del navegador con el resultado. Escucho un evento de "carga" en el iframe, con la esperanza de que se active cuando se complete la descarga.
Devuelvo un encabezado "Content-Disposition: adjunto" con el archivo, lo que hace que el navegador muestre el cuadro de diálogo "Guardar". Pero el navegador no dispara un evento de "carga" en el iframe.
Un enfoque que probé es usar una respuesta de varias partes. Por lo tanto, enviaría un archivo HTML vacío, así como el archivo descargable adjunto. Por ejemplo:
Content-type: multipart/x-mixed-replace;boundary="abcde"
--abcde
Content-type: text/html
--abcde
Content-type: application/vnd.fdf
Content-Disposition: attachment; filename=foo.fdf
file-content
--abcde
Esto funciona en Firefox; recibe el archivo HTML vacío, activa el evento "cargar", luego muestra el cuadro de diálogo "Guardar" para el archivo descargable. Pero falla en IE y Safari; IE dispara el evento "cargar" pero no descarga el archivo, y Safari descarga el archivo (con el nombre y tipo de contenido incorrectos) y no dispara el evento "cargar".
Un enfoque diferente podría ser hacer una llamada para comenzar la creación del archivo, luego sondear el servidor hasta que esté listo, luego descargar el archivo ya creado. Pero prefiero evitar crear archivos temporales en el servidor.
¿Alguien tiene una idea mejor?
fuente
Respuestas:
Una posible solución utiliza JavaScript en el cliente.
El algoritmo del cliente:
El algoritmo del servidor:
Código fuente del cliente (JavaScript):
Ejemplo de código de servidor (PHP):
Dónde:
fuente
Una solución de una línea muy simple (y poco convincente) es usar el
window.onblur()
evento para cerrar el diálogo de carga. Por supuesto, si lleva demasiado tiempo y el usuario decide hacer otra cosa (como leer correos electrónicos), se cerrará el cuadro de diálogo de carga.fuente
onbeforeunload
Gracias.hilo viejo, lo sé ...
pero aquellos que están dirigidos aquí por Google podrían estar interesados en mi solución. Es muy simple, pero confiable. y permite mostrar mensajes de progreso real (y se puede conectar fácilmente a los procesos existentes):
el script que procesa (mi problema era: recuperar archivos a través de http y entregarlos como zip) escribe el estado en la sesión.
el estado se sondea y se muestra cada segundo. eso es todo (está bien, no. Tienes que ocuparte de muchos detalles [por ejemplo, descargas simultáneas], pero es un buen lugar para comenzar ;-)).
la página de descarga:
getstatus.php
download.php
fuente
.each()
para inscribirse en eventos. solo diga$('a.download').click()
setTimeout('getstatus()', 1000);
. Use el fn directamente:setTimeout(getstatus, 1000);
Utilizo lo siguiente para descargar blobs y revocar la URL del objeto después de la descarga. Funciona en Chrome y Firefox!
después de que se cierra el cuadro de diálogo de descarga de archivos, la ventana recupera su enfoque para que se active el evento de enfoque.
fuente
Basado en el ejemplo de Elmer, he preparado mi propia solución. Después de hacer clic en los elementos con una clase de descarga definida , permite mostrar un mensaje personalizado en la pantalla. He usado desencadenador de foco para ocultar el mensaje.
JavaScript
HTML
Ahora debe implementar cualquier elemento para descargar:
o
Después de cada clic de descarga , verá un mensaje que está creando su informe, espere ...
fuente
Escribí una clase simple de JavaScript que implementa una técnica similar a la descrita en una respuesta tormentosa . Espero que pueda ser útil para alguien aquí. El proyecto GitHub se llama response-monitor.js
De forma predeterminada, utiliza spin.js como indicador de espera, pero también proporciona un conjunto de devoluciones de llamada para la implementación de un indicador personalizado.
JQuery es compatible pero no es obligatorio.
Características notables
Ejemplo de uso
HTML
Cliente (JavaScript simple)
Cliente (JQuery)
Cliente con devoluciones de llamada (JQuery)
Servidor (PHP)
Para obtener más ejemplos, consulte la carpeta de ejemplos en el repositorio.
fuente
Llego muy tarde a la fiesta, pero pondré esto aquí si alguien más quiere saber mi solución:
Tuve una verdadera lucha con este problema exacto, pero encontré una solución viable usando iframes (lo sé, lo sé. Es terrible, pero funciona para un problema simple que tenía)
Tenía una página html que lanzó un script php separado que generó el archivo y luego lo descargó. En la página html, utilicé el siguiente jquery en el encabezado html (también necesitarás incluir una biblioteca jquery):
En your_download_script.php, tenga lo siguiente:
Para desglosar esto, jquery primero lanza su script php en un iframe. El iframe se carga una vez que se genera el archivo. Luego, jquery lanza el script nuevamente con una variable de solicitud que le dice al script que descargue el archivo.
La razón por la que no puede realizar la descarga y la generación de archivos de una sola vez se debe a la función php header (). Si usa el encabezado (), está cambiando el script a algo diferente a una página web y jquery nunca reconocerá el script de descarga como 'cargado'. Sé que esto no necesariamente se detecta cuando un navegador recibe un archivo, pero su problema sonó similar al mío.
fuente
Si está transmitiendo un archivo que está generando dinámicamente y también tiene implementada una biblioteca de mensajería de servidor a cliente en tiempo real, puede alertar a su cliente con bastante facilidad.
La biblioteca de mensajería de servidor a cliente que me gusta y recomiendo es Socket.io (a través de Node.js). Después de que el script de su servidor haya terminado de generar el archivo que se está transmitiendo para descargar su última línea en ese script, puede emitir un mensaje a Socket.io que envía una notificación al cliente. En el cliente, Socket.io escucha los mensajes entrantes emitidos por el servidor y le permite actuar sobre ellos. El beneficio de usar este método sobre otros es que puede detectar un evento de finalización "verdadero" después de que se realiza la transmisión.
Por ejemplo, podría mostrar su indicador de ocupado después de hacer clic en un enlace de descarga, transmitir su archivo, emitir un mensaje a Socket.io desde el servidor en la última línea de su secuencia de comandos de transmisión, escuchar una notificación en el cliente, recibir la notificación y actualice su IU ocultando el indicador de ocupado.
Me doy cuenta de que la mayoría de las personas que leen respuestas a esta pregunta pueden no tener este tipo de configuración, pero he usado esta solución exacta con gran efecto en mis propios proyectos y funciona de maravilla.
Socket.io es increíblemente fácil de instalar y usar. Ver más: http://socket.io/
fuente
"¿Cómo detectar cuándo el navegador recibe la descarga de archivos?"
Me enfrenté al mismo problema con esa configuración:
struts 1.2.9
jquery-1.3.2.
jquery-ui-1.7.1.custom
IE 11
java 5
Mi solución con una cookie:
- Lado del cliente:
cuando envíe su formulario, llame a su función de JavaScript para ocultar su página y cargar su ruleta en espera
Luego, llame a una función que verificará cada 500 ms si una cookie proviene del servidor.
Si se encuentra la cookie, deje de verificar cada 500 ms, caduque la cookie y llame a su función para volver a su página y eliminar la rueda de espera ( removeWaitingSpinner () ). ¡Es importante que caduque la cookie si desea poder descargar otro archivo nuevamente!
- Lado del servidor:
al final del proceso del servidor, agregue una cookie a la respuesta. Esa cookie se enviará al cliente cuando su archivo esté listo para descargar.
Espero ayudar a alguien!
fuente
Cuando el usuario activa la generación del archivo, simplemente puede asignar una ID única a esa "descarga" y enviar al usuario a una página que se actualiza (o verifica con AJAX) cada pocos segundos. Una vez que el archivo esté terminado, guárdelo con esa misma ID única y ...
Luego puede omitir todo el desorden de iframe / waiting / browserwindow, y aún así tener una solución realmente elegante.
fuente
Si no desea generar y almacenar el archivo en el servidor, ¿está dispuesto a almacenar el estado, por ejemplo, archivo en progreso, archivo completo? Su página de "espera" podría sondear al servidor para saber cuándo se completa la generación del archivo. No sabría con certeza si el navegador inició la descarga, pero tendría cierta confianza.
fuente
Acabo de tener exactamente el mismo problema. Mi solución fue usar archivos temporales ya que ya estaba generando un montón de archivos temporales. El formulario se envía con:
Al final del script que genera el archivo que tengo:
Esto hará que se active el evento de carga en el iframe. Luego se cierra el mensaje de espera y se iniciará la descarga del archivo. Probado en IE7 y Firefox.
fuente
En mi experiencia, hay dos formas de manejar esto:
fuente
Saludos, sé que el tema es antiguo pero dejo una solución que vi en otro lugar y funcionó:
Puedes usarlo:
fuente
Si ha descargado un archivo, que se guarda, en lugar de estar en el documento, no hay forma de determinar cuándo se completa la descarga, ya que no está dentro del alcance del documento actual, sino un proceso separado en el navegador.
fuente
La pregunta es tener un indicador de 'espera' mientras se genera un archivo y luego volver a la normalidad una vez que el archivo se esté descargando. La forma en que me gusta hacer esto es usar un iFrame oculto y conectar el evento de carga del marco para que mi página sepa cuándo comienza la descarga. PERO la carga no se dispara en IE para descargas de archivos (como con el token del encabezado del archivo adjunto). El sondeo del servidor funciona, pero no me gusta la complejidad adicional. Entonces, esto es lo que hago:
Descargo de responsabilidad, no haga esto en un sitio ocupado, ya que el almacenamiento en caché podría acumularse. Pero realmente, si sus sitios que están ocupados con el proceso de larga ejecución lo dejarán sin hilos de todos modos.
Así es como se ve el código subyacente, que es todo lo que realmente necesita.
fuente
Una solución rápida si solo desea mostrar un mensaje o un gif de cargador hasta que aparezca el cuadro de diálogo de descarga es colocar el mensaje en un contenedor oculto y cuando hace clic en el botón que genera el archivo a descargar, hace que el contenedor sea visible. Luego use jquery o javascript para capturar el evento de enfoque del botón para ocultar el contenedor que contiene el mensaje
fuente
Si Xmlhttprequest con blob no es una opción, puede abrir su archivo en una nueva ventana y verificar si los elementos eny se llenan en ese cuerpo de ventana con intervalo.
fuente
Este ejemplo de Java / Spring detecta el final de una descarga, en cuyo punto oculta el indicador "Cargando ...".
Enfoque: en el lado de JS, configure una cookie con una edad de caducidad máxima de 2 min y realice una encuesta cada segundo para la caducidad de la cookie . Luego, el lado del servidor anula esta cookie con una edad de vencimiento anterior : la finalización del proceso del servidor. Tan pronto como se detecta la caducidad de la cookie en el sondeo de JS, se oculta "Cargando ...".
JS Side
Java / Spring Server Side
fuente
myCookie.setPath("/"); response.addCookie(myCookie);
Primefaces también utiliza encuestas de cookies
https://github.com/primefaces/primefaces/blob/32bb00299d00e50b2cba430638468a4145f4edb0/src/main/resources/META-INF/resources/primefaces/core/core.js#L458
fuente
Cree un iframe cuando haga clic en el botón / enlace y añádalo al cuerpo.
Cree un iframe con retraso y elimínelo después de la descarga.
fuente