在HTML5浏览器中取消单个图像请求

22
我将动态加载(大)图像以绘制到html5画布中,就像这样:
var t = new Image();
t.onload = ...
t.src = 'http://myurl';

但有时候我想完全取消图片请求

我找到的唯一方法是将src设置为''。即:

t.src = ''

这在许多浏览器中都有效,但似乎只有Firefox实际上取消了图片的http请求。

我使用Fiddler2进行测试,禁用缓存并启用“模拟调制解调器速度”。然后运行一个fiddle来测试取消图片请求。(我很想听听其他测试方法)

我知道有一些方法可以取消所有请求(就像这个问题中提到的那样),但我想只取消一个请求。

您有没有其他方法(尤其是在移动浏览器上)可以做到这一点?


4
小心使用t.src = '',规范对应该发生什么并不明确。请参见http://www.nczonline.net/blog/2009/11/30/empty-image-src-can-destroy-your-site/。 - Crescent Fresh
1
好的...一些浏览器尝试通过页面的URL(或基本URL)发送请求来加载src=''图像。在Firefox下,设置t.src=null也会取消请求,但在其他浏览器上不起作用。较新的(当前工作中的)规范在某些情况下表明它应该触发onerror...有点这样。 - Amir
3
在Firefox中将src设置为内联图片也会取消先前的请求:t.src='data:image/gif;base64,R0lGOD... 可以在这个fiddle中查看。 - Amir
空图像源会使页面重新加载,请求会让你回到页面。因此,当您在提交后抵达页面时,浏览器将会默默地重新提交该POST操作。这确实是一种自毁长城的好方法。 - regilero
4个回答

15

这是我成功在所有现代浏览器(Chrome、Safari、Mobile-Safari、Firefox 和 IE9)中让其工作的唯一方法:

  • 加载一个空的、隐藏的 iframe
  • 将一个 image tag 添加到 iframe 中的 body
  • img.onload 完成时,您可以使用该图像 dom 元素使用 drawImage() 绘制到 html5 画布上
  • 如果要取消加载,请在 iframecontentWindow 上发出 stop()(或者在 IE 中,在 contentDocument 上执行 execCommand("stop", false))。
  • 在取消图像加载之后,可以重复使用 iframe 加载更多图像。

我为此创建了一个类,并将 coffeescript 代码(及其编译后的 javascript)放在了 Github 上:Cancelable Html5 Image Loader

如果您想尝试它,我还创建了一个 jsfiddle 来测试它。请记住启动 Fiddler2(或类似的工具)以查看实际的网络请求是否被真正取消。


+1 谢谢那个技巧。如果能找到一种不需要canvas的方式为旧版IE提供某些东西,可能会很好,也许可以使用添加canvas支持的库来实现。 - regilero
@Amir,我们如何将此实现到OpenLayers瓦片请求中? - Myra
@Amir 我遇到了同样的问题,尝试复制你的脚本,但是我无法让iframe.contentWindow.stop()方法起作用。你能否帮忙看一下? - Ming

4
您可以尝试使用window.stop()停止所有请求,但无法停止单个请求。在IE中,不是window.stop()而是document.execCommand("Stop", false)。
如果您有其他请求相关的操作,则这样做会干扰它们。(您需要重新请求仍然需要的资源,但这太麻烦了)。
因此,如果您想要停止大型图像的单个请求,可以将图像加载到隐藏的IFRAME文档中。IFRAME的onload事件可用于将图像加载到主文档中,此时应该已缓存(假设您已配置缓存指令)。
如果图像花费的时间太长,则可以访问IFRAME的contentWindow并向其发出停止命令。
您需要拥有与同时请求的图像数量相同的IFRAME元素。

“但不包括个别的”...你能支持这个吗?你提供的其他信息已经在问题中说明并链接了。 - Amir
你只能停止窗口中正在进行的所有请求。这给了我一个想法,请查看我答案中添加的附加信息! - Lee Kowalkowski

2

我实际上也遇到了同样的问题,并进行了一些测试。

我使用 iframes 进行了一些测试,并且取得了一些成功。在 Firefox 3.6 和 Chrome 上进行了测试。对于这些测试,我使用了 9 张大小为 15Mb 的图片。使用 img 预加载会使我的 firefox 崩溃数次(是的,在这台电脑上内存不多),使用 img,“suspend” 操作并不能阻止图片加载,如果请求已经开始,而使用 iframes,“suspend” 操作可以真正停止下载(因为我删除了 iframe)。下一步将测试 XMLHttpRequests。由于这个版本的测试代码使用了浏览器缓存行为来防止图像 url 的第二次加载,并且这也适用于 ajax 请求。但也许使用 iframes,我可以找到一种直接从加载的 iframe 中检索二进制数据的方法(受同域 url 的限制)。

这是我的测试代码(在 Chrome 上非常快,但可能会因为太多非常大的图片而使您的 firefox 崩溃):

// 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');
            });
    });
});

编辑,这里是fillde用来测试的链接:http://jsfiddle.net/wPr3x/


非常接近了,但是出现了问题。由于某种原因,当图像在 iframe 中加载时,它不会立即在缓存中可用。因此,图像最终被加载两次。这里有一个可以玩的 fiddle。http://jsfiddle.net/amirshim/na3UA/ 启动 Fiddler2(在 PC 上),看看图像是如何被请求两次的。如果关闭 use_unique_name,那么您将看到在第一次加载整个页面后,图像被缓存了。 - Amir
我创建了一个新的问题来尝试解决这个问题:https://dev59.com/GlTTa4cB1Zd3GeqPq0Zd - Amir
在这个小提琴示例中,我没有看到你在设置src属性并插入iframe之后设置load()回调函数的地方。因此,您可能会在加载“about:blank”或类似内容即空的iframe基本资源之后立即调用此加载事件。 - regilero
我使用 loader.onload = 而不是 jQuery 的 load()。但这里有一个使用 jQuery 方法的 fiddle,它有同样的问题:http://jsfiddle.net/amirshim/na3UA/1/ - Amir
在其他问题中的回答中,我认为如果你像我一样编写它,它可以工作。这里有一个fiddle来测试它,但要小心更改图像URL,以防止针对目标服务器的DOS攻击。http://jsfiddle.net/wPr3x/ - regilero

2

我怀疑没有一种跨浏览器的方法可以在不使用hack的情况下实现这一点。一个建议是向服务器端脚本发出AJAX请求,该脚本返回一个Base64编码版本的图像,您可以将其设置为src属性。然后,如果您决定取消加载图像,则可以取消此请求。不过,如果您想逐步显示图像,这可能会很困难。


1
好主意,但是这个方案存在一些问题。首先,传输的数据量增加了33%(base64:24位->32位)。其次(特别是在移动浏览器上),它会给JavaScript带来很大的内存压力,因为数据必须以字符串形式存储在JS中,然后还需要进行额外的base64解码步骤。对于大文件(200K+),这可能会有所影响。但在某些情况下,这肯定可以很好地发挥作用。 - Amir
这是一个很好的观点。当我想出这个想法时,我没有考虑到开销。显然,已经有一些人尝试使用AJAX读取二进制文件http://nagoon97.wordpress.com/2008/04/06/reading-binary-files-using-ajax/。我自己从未尝试过这样做,但你可以潜在地这样做,然后使用`<canvas>`绘制图像。 - Michael Mior

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接