【问题标题】:How can I revoke an object URL only after it's downloaded?如何仅在下载对象 URL 后撤销它?
【发布时间】:2016-09-11 10:53:38
【问题描述】:

我正在使用以下代码在 JavaScript 中保存一个文件:

var a = document.createElement('a');
a.href = URL.createObjectURL(new Blob(['SOME DATA']));
a.download = 'some.dat';
a.click();

我想在文件下载后撤销 URL(使用 URL.revokeObjectURL)。什么时候这样做是安全的?

我可以在致电a.click() 后立即撤销它吗(这似乎有效,但我不确定它是否安全)?在a 的点击事件监听器中?有没有办法让点击事件监听器在默认操作之后运行

【问题讨论】:

    标签: javascript cross-browser bloburls


    【解决方案1】:

    DOM 元素上的a.click() 模拟对元素的点击,而不是传播点击事件,因此它直接发送到浏览器。我相信使用计时器将 URL 对象的撤销转移到另一个事件周期会更安全一些:

    setTimeout(function() {
     URL.revokeObjectURL(a.href);
    }, 0);
    

    【讨论】:

    • 你的意思是setTimeout
    • click() 确实会触发事件——参见MDN。这对我来说意味着它是异步的,因此将在当前 JS 执行周期之后运行。
    • 你好史蒂夫,你是对的。我认为 click() 内部会像普通用户的点击一样添加到事件循环点击事件(根据 MDN 仅冒泡阶段)。但是通过设置计时器,我希望您在处理这些事件之后推迟撤销 URL 对象。但是你总是可以设置更高的 tiemout :)
    • 有了这个,您可能会在下载文件之前将其删除。一个非常糟糕的建议,绝对不是这个问题的解决方案。
    • 查看stackoverflow.com/a/66905746/470749以获得完整答案。
    【解决方案2】:

    经过一些实验,Chrome 和 Safari 似乎都能够在单击元素后立即撤销时下载 2GB 的文件。 Firefox 能够在浏览器停止运行之前下载 600MB 的文件。

    这是我用来下载大文件的:

    const a = document.createElement('a');
    const buffer = new ArrayBuffer(2_000_000_000);
    const view = new Uint8Array(buffer);
    for(let i=0; i<view.length; i++) {
        view[i] = 255;
    }
    a.href = URL.createObjectURL(new Blob([buffer]));
    a.download = 'some.dat';
    a.click();
    URL.revokeObjectURL(a.href);
    

    规范没有特别提到在撤销 url 时中止现有流,所以理论上这样做就可以了。

    但是,为了安全起见,我会在几秒钟后使用 setTimeout() 撤消 url,或者如果从特定屏幕启动下载,您可以添加逻辑以在用户离开该屏幕后撤消它。

    一旦您的域的最后一页关闭,浏览器也会自动撤销对象 url,因此根据您的情况,根本不撤销 url 也可能是一个可行的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-04-12
      • 1970-01-01
      • 1970-01-01
      • 2015-04-16
      • 1970-01-01
      • 1970-01-01
      • 2015-11-17
      相关资源
      最近更新 更多