何时可以安全地调用 URL.revokeObjectURL

When is it safe to call URL.revokeObjectURL?

本文关键字:URL revokeObjectURL 调用 安全 何时可      更新时间:2023-09-26

如果我理解正确,URL.createObjectURL会创建一个表示文件或 blob 的 URL。由于 URL 只是一个字符串,因此浏览器无法知道您何时完成 URL 表示的资源,因此提供了URL.revokeObjectURL函数。

MDN 显示了这个例子:

var canvas = document.getElementById("canvas");
canvas.toBlob(function(blob) {
  var newImg = document.createElement("img");
  var url = URL.createObjectURL(blob);
  newImg.onload = function() {
    // no longer need to read the blob so it's revoked
    URL.revokeObjectURL(url);
  };
  newImg.src = url;
  document.body.appendChild(newImg);
});

所以一些问题

  1. 在分配 URL 后立即更改代码以撤销 URL 是否安全newImg.src

    var canvas = document.getElementById("canvas");
    canvas.toBlob(function(blob) {
      var newImg = document.createElement("img");
      var url = URL.createObjectURL(blob);
      newImg.src = url;
      // no longer need to read the blob as it's assigned to newImg.src
      URL.revokeObjectURL(url);
      document.body.appendChild(newImg);
    });
    

    我猜答案是否定的,因为可能没有任何开始newImg.src = url;.在这一点上,它仍然只是一个字符串,并且将一直保持到当前 JavaScript 事件退出。还是吗?

  2. 撤销 URL 但仍然使用它知道它被其他对象引用是否有效/合法/正确?

    var canvas = document.getElementById("canvas");
    canvas.toBlob(function(blob) {
      var newImg = document.createElement("img");
      var url = URL.createObjectURL(blob);
      newImg.onload = function() {
        // no longer need to read the blob so it's revoked
        URL.revokeObjectURL(url);
        // Use the URL again even though it's revoked
        var newImg2 = new Image():
        newImg2.src = url; 
      };
      newImg.src = url;
      document.body.appendChild(newImg);
    });
    

    在这种情况下,即使我已经撤销了 URL,我也会分配newImg2.src = url。这个想法是newImg仍然引用 blob URL,因此能够说它似乎是有效的

    someImage.src = someOtherImage.src
    

    在任何时候。是吗?

好吧,好吧,按照@adeneo的建议,我测试了这个

$('#test').on('change', function(e) {
	var newImg = document.createElement("img");
    var url = URL.createObjectURL( e.target.files[0] )
    console.log(url);
    
    newImg.src = url;
    URL.revokeObjectURL(url);
    document.body.appendChild(newImg);
    console.log(url);
});
$('#test3').on('change', function(e) {
	var newImg = document.createElement("img");
    var url = URL.createObjectURL( e.target.files[0] )
    console.log(url);
    
    newImg.src = url;
    newImg.onload = function() {
    	URL.revokeObjectURL(url);
    	document.body.appendChild(newImg);
      var i = new Image();
      i.src=  newImg.src;
      document.body.appendChild(i);
      setTimeout(function() {
        var g = new Image();
        g.src = newImg.src;
        document.body.appendChild(g);
      }, 3000);
    }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>test revoke before use</p>
<input type="file" id="test"/>
<br />
<br />
<br />
<p>test use revoke use</p>
<input type="file" id="test3" />

至少在 Firefox 中,一旦调用URL.revokeObjectURL,即使其他东西正在访问它,也无法再使用该 URL。

因此,问题中的 #1 和 #2 都失败了,即使在 #2 中,newImg.src仍然具有 URL,一旦调用URL.revokeObjectURL URL,该 URL 将无法在其他任何地方工作。

在 React 中,有一个名为 useEffect 的钩子,它所做的一件事是,在你离开组件(函数(之前你想采取什么操作,在那里你释放了之前通过调用 URL.createObjectURL(( 创建的现有对象 URL。

使用 URL.revokeObjectURL 释放现有对象 URL:

useEffect(() => () => URL.revokeObjectURL(file as string), [file])
const onChangeFile = (e: React.ChangeEvent<HTMLInputElement>) => {
    if (e.target.files[0]) {
        setFile(URL.createObjectURL(e.target.files[0]))
    }
}