2011-02-07 21 views
21

Estoy cargando (grandes) imágenes de forma dinámica para dibujar en un lienzo HTML5, algo como esto:Cancelar solicitud única imagen en los navegadores HTML5

var t = new Image(); 
t.onload = ... 
t.src = 'http://myurl'; 

Pero de vez en cuando nos gustaría cancelar la imagen solicitar por completo.

La única forma en que se me ocurrió establecer src en ''. es decir,

t.src = '' 

Esto funciona en muchos navegadores, pero parece que sólo Firefox realidad cancela la solicitud HTTP para la imagen.

Probé esto con Fiddler2 desactivando el almacenamiento en caché y habilitando "emular la velocidad del módem". Luego ejecutando a fiddle to test canceling a image request. (me gustaría escuchar otras ideas sobre cómo probar esto)

Sé que hay formas de cancelar todas las solicitudes (as in this question), pero me gustaría cancelar solo una.

¿Alguna idea sobre otras formas (especialmente en los navegadores móviles) de hacer esto?

+4

Tenga cuidado con 't.src = ''', la especificación es vaga sobre lo que debería suceder. Consulte http://www.nczonline.net/blog/2009/11/30/empty-image-src-can-destroy-your-site/ –

+1

Derecha ... algunos de los navegadores realizan solicitudes a la URL de la página (o URL base) para intentar cargar la imagen con 'src = '''. En Firefox, establecer 't.src = null' también cancela la solicitud, pero no hace nada en otros. La especificación más reciente (trabajo actual) dice (en algunas situaciones) que [debe disparar contra el error] (http://www.whatwg.org/specs/web-apps/current-work/multipage/embedded-content-1 .html # update-the-image-data) ... kindof. – Amir

+3

Establecer 'src' en una imagen en línea en Firefox también cancela la solicitud anterior:' t.src = 'data: image/gif; base64, R0lGOD ... 'véalo en [este violín] (http: // jsfiddle. net/amirshim/F4HbT/21 /) – Amir

Respuesta

13

Esta es la única forma en que logré que funcione en todos los navegadores modernos (Chrome, Safari, Mobile-Safari, Firefox e IE9).

  • Cargar un vacío y ocultos iframe
  • anexar un image tag a la body en el iframe
  • cuando los img.onload finalice, puede utilizar ese elemento de imagen dom llamar a un canvas de HTML5 con drawImage()
  • si desea cancelar la carga, emita un stop() en iframe 's contentWindow (o execCommand("stop", false) en contentDocument en IE).
  • después de cancelar la carga de una imagen, puede reutilizar el iframe para cargar más imágenes.

creé una clase para esto, y poner el código CoffeeScript (y es compilado JavaScript) en github: Cancelable Html5 Image Loader

Si quiere jugar con él, también creó un jsfiddle to test it out. Recuerde iniciar Fiddler2 (o algo similar) para ver que la solicitud de red real realmente se está cancelando.

+0

+1 gracias por ese truco. Sería bueno encontrar una manera de proporcionar algo para la versión anterior de IE sin lienzo, tal vez con las librerías que agregan soporte de lienzo en IE. – regilero

+0

@ Amir, ¿cómo podemos implementar esto en solicitudes de mosaico de capas abiertas? – Myra

+0

@Amir Obtuve el mismo problema e intenté replicar su script, pero no puedo hacer que funcione el método iframe.contentWindow.stop(). ¿Hay alguna manera de echarle un vistazo? – Ming

4

Puede intentar window.stop() para detener todas las solicitudes, pero no las individuales. En IE, no es window.stop() es document.execCommand ("Stop", falso).

Si tiene otras cosas en curso, entonces interferir con esto. (Haría un seguimiento con una nueva solicitud de recursos que aún desea, pero eso es demasiado trabajo duro).


Así que si quería dejar una sola solicitud de una imagen de gran tamaño, lo que podría hacer es cargar la imagen en un documento dentro de un iframe oculto. El evento de carga del IFRAME se puede usar para cargar la imagen en el documento principal, momento en el que debe almacenarse en caché (suponiendo que tiene las directivas de caché configuradas para hacerlo).

Si la imagen tarda demasiado, puede acceder al ContentWindow de IFRAME y emitir un comando de detención para eso.

Necesita tener tantos elementos IFRAME como imágenes que se puedan solicitar simultáneamente.

+0

"pero no individuales" ... ¿puedes respaldar esto? El resto de la información que proporcionó ya estaba indicada y vinculada a la pregunta. – Amir

+0

Solo puede detener todas las solicitudes en curso para la ventana. Lo cual me da una idea, ¡ve información adicional agregada a mi respuesta! –

2

Dudo que haya una forma de navegador cruzado para que esto ocurra sin hacks. Una sugerencia es realizar una solicitud AJAX a un script del lado del servidor que devuelve una versión codificada en Base64 de la imagen que luego puede establecer como el atributo src. Luego puede cancelar esta solicitud si decide cancelar la carga de la imagen. Sin embargo, esto podría ser difícil si desea mostrar la imagen progresivamente.

+1

Excelente idea, pero hay un par de problemas asociados con esto. Primero, la cantidad de datos transferidos es un 33% más. (base64: 24 bits -> 32 bits). Luego (especialmente en navegadores móviles), pone mucha presión de memoria en javascript, ya que los datos deben almacenarse como una cadena en JS y luego también tiene un paso adicional de decodificación base64. Para archivos grandes (200 K +) esto podría doler. Pero esto definitivamente podría funcionar bien en algunos escenarios. – Amir

+0

Ese es un buen punto. Cuando se me ocurrió la idea, no pensé en los gastos generales.Aparentemente ha habido algún esfuerzo en leer archivos binarios usando AJAX http://nagoon97.wordpress.com/2008/04/06/reading-binary-files-using-ajax/. Nunca lo intenté yo mismo, pero podrías hacer esto y luego dibujar la imagen usando un ''. –

2

Tengo el mismo problema y realicé algunas pruebas.

He hecho algunas pruebas con iframes y tengo cierto éxito. Probado en Firefox 3.6 y Chrome. Para las pruebas que utilicé 9 imágenes de 15Mb. Con la precarga de img maté a mi firefox varias veces (sí, no tengo mucha memoria en esta computadora), usar img la acción "suspender" no evita la carga de imágenes si la solicitud ha comenzado, con iframes la acción de suspensión realmente detiene la descarga (como yo eliminar el iframe). El siguiente paso será probar con XMLHttpRequests.Como esta versión de mi código de prueba usa el comportamiento del caché braowser, forma la url de la imagen para evitar una segunda carga y esto también funciona con las solicitudes ajax. Pero tal vez con iframes podría encontrar una manera de recuperar datos binarios de la imagen cargada en iframe directamente (con limitación en las mismas URL de dominio).

Este es mi código de prueba (muy rápido en Chrome, puede matar a tu Firefox con demasiada muy grandes imágenes):

// jQuery.imageload.shared.list contains an array of oversized images url 
jQuery.each(imglist,function(i,imgsrc) { 

    name = 'imageload-frame'; 
    id = name + "-" + i; 
    // with img it is not possible to suspend, the get is performed even after remove 
    //var loader = jQuery('<img />').attr('name', name).attr('id',id); 
    var loader = jQuery('<iframe />').attr('name', name).attr('id',id); 

    //no cache on GET query taken from jQuery core 
    // as we really want to download each image for these tests 
    var ts = +new Date; 
    // try replacing _= if it is there 
    var ret = imgsrc.replace(/(\?|&)_=.*?(&|$)/, "$1_=" + ts + "$2"); 
    // if nothing was replaced, add timestamp to the end 
    imgsrc = imgsrc + ((ret == imgsrc) ? (imgsrc.match(/\?/) ? "&" : "?") + "_=" + ts : ""); 

    loader.css('display', 'none').appendTo('body'); 
    loader.after(jQuery('<a>') 
      .text(' stop ') 
      .attr('href','#') 
      .click(function(){ 
       loader.remove(); 
       jQuery(this).text(' suspended '); 
      }) 
    ); 

    // start the load - preload 
    loader.attr('src',imgsrc); 

    // when preload is done we provide a way to get img in document 
    loader.load(function() { 
     jQuery(this).next("a:first") 
      .text(" retrieve ").unbind('click') 
      .click(function() { 
       var finalimg = jQuery('<img />'); 
       // browser cache should help us now 
       // but there's maybe a way to get it direclty from the iframe 
       finalimg.attr('src',loader[0].src); 
       finalimg.appendTo('body'); 
      }); 
    }); 
}); 

edición y aquí se fillde para probarlo: http://jsfiddle.net/wPr3x/

+0

Tan cerca, pero hay un problema. Por alguna razón, cuando la imagen se carga en el iframe, no está inmediatamente disponible en la memoria caché. entonces la imagen termina siendo cargada dos veces. aquí hay un violín para jugar. http://jsfiddle.net/amirshim/na3UA/ Inicie fiddler2 (en una pc) y vea cómo se solicita la imagen dos veces. Si desactiva 'use_unique_name', verá que la imagen se almacena en caché después de la primera carga de la página. – Amir

+0

Creé una nueva pregunta para intentar resolver ese problema: http://stackoverflow.com/questions/4981229/image-not-loading-from-cache-after-its-loaded-in-an-iframe – Amir

+0

on th fiddle ejemplo, no veo dónde se establece la devolución de llamada load() antes de configurar el atributo src y luego de insertar el iframe. Por lo tanto, puede hacer que se llame a este evento de carga justo después de la carga de "about: blank" o tal, es decir, la fuente base de iframe vacía. – regilero

Cuestiones relacionadas