如何仅在 URL 加载后调用 revokeObjectURL?
How can I call revokeObjectURL only after the URL has loaded?
我有一个网络应用程序。它在 Google Chrome 中运行,不需要在任何其他浏览器中工作。
我有在服务器上生成并在 AJAX 请求中发送回客户端的 PDF 数据。
我从 PDF 数据创建了一个 blob。
我使用 window.URL.createObjectURL 从 blob 创建一个 URL,然后我将其加载到一个 window(我的 preview_window)中,该 window 之前是为显示 PDF 而创建的。
为了加载 URL,我设置了 preview_window.location.href。
我想调用 revokeObjectURL 以避免在 window 中生成和预览新 PDF 时浪费越来越多的资源。
问题是在设置 preview_window.location.href 后立即调用它太早了,并且会停止显示 PDF。所以我只想在 URL 加载后调用 revokeObjectURL 。
为此,我曾尝试将 preview_window.onload 设置为回调,但它从未被调用过。
我想知道:
- 当 window 加载 URL 时是否可以触发回调,正如我正在尝试做的那样?怎么样?
- 是否有其他方法可以确保及时调用 revokeObjectURL?
如果我无法在 window 完成加载 URL 时触发 revokeObjectURL,我可能会在生成新对象之前立即撤销每个 URL。但我宁愿在加载完成后立即撤销 URL,如果可能的话。
我准备了一个 html 文件,它很好地说明了情况:
<html>
<head>
<title>Show PDF Demo</title>
<script>
var build_blob = function(mime_type, data) {
var buf = new ArrayBuffer(data.length);
var ia = new Uint8Array(buf);
for (var i = 0; i < data.length; i++) ia[i] = data.charCodeAt(i);
var blob = new Blob([ buf ], { type: mime_type });
return blob;
};
window.onload = function(e) {
document.getElementById('preview_button').onclick = function(e) {
// open the window in the onclick handler so we don't trigger popup blocking
var preview_window = window.open(null, 'preview_window');
// use setTimeout to simulate an asynchronous AJAX request
setTimeout(function(e) {
var pdf_data = atob(
"JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZwovT3V0bGluZXMgMiAwIFIKL1BhZ2Vz" +
"IDMgMCBSCj4+CmVuZG9iagoyIDAgb2JqCjw8IC9UeXBlIC9PdXRsaW5lcwovQ291bnQgMAo+Pgpl" +
"bmRvYmoKMyAwIG9iago8PCAvVHlwZSAvUGFnZXMKL0tpZHMgWzQgMCBSXQovQ291bnQgMQo+Pgpl" +
"bmRvYmoKNCAwIG9iago8PCAvVHlwZSAvUGFnZQovUGFyZW50IDMgMCBSCi9NZWRpYUJveCBbMCAw" +
"IDUwMCAyMDBdCi9Db250ZW50cyA1IDAgUgovUmVzb3VyY2VzIDw8IC9Qcm9jU2V0IDYgMCBSCi9G" +
"b250IDw8IC9GMSA3IDAgUiA+Pgo+Pgo+PgplbmRvYmoKNSAwIG9iago8PCAvTGVuZ3RoIDczID4+" +
"CnN0cmVhbQpCVAovRjEgMjQgVGYKMTAwIDEwMCBUZAooU01BTEwgVEVTVCBQREYgRklMRSkgVGoK" +
"RVQKZW5kc3RyZWFtCmVuZG9iago2IDAgb2JqClsvUERGIC9UZXh0XQplbmRvYmoKNyAwIG9iago8" +
"PCAvVHlwZSAvRm9udAovU3VidHlwZSAvVHlwZTEKL05hbWUgL0YxCi9CYXNlRm9udCAvSGVsdmV0" +
"aWNhCi9FbmNvZGluZyAvTWFjUm9tYW5FbmNvZGluZwo+PgplbmRvYmoKeHJlZgowIDgKMDAwMDAw" +
"MDAwMCA2NTUzNSBmCjAwMDAwMDAwMDkgMDAwMDAgbgowMDAwMDAwMDc0IDAwMDAwIG4KMDAwMDAw" +
"MDEyMCAwMDAwMCBuCjAwMDAwMDAxNzkgMDAwMDAgbgowMDAwMDAwMzY0IDAwMDAwIG4KMDAwMDAw" +
"MDQ2NiAwMDAwMCBuCjAwMDAwMDA0OTYgMDAwMDAgbgp0cmFpbGVyCjw8IC9TaXplIDgKL1Jvb3Qg" +
"MSAwIFIKPj4Kc3RhcnR4cmVmCjYyNQolJUVPRg=="
);
/*
Warning: for my Chrome (Version 44.0.2403.155 m), the in-built PDF viewer doesn't seem
to work with a blob when this html page is loaded from the local filesystem. I have only
got this to work when fetching this page via HTTP.
*/
var pdf_blob = build_blob('application/pdf', pdf_data);
var pdf_url = window.URL.createObjectURL(pdf_blob);
preview_window.onload = function(e) {
console.log("preview_window.onload called"); // never happens
window.URL.revokeObjectURL(pdf_url);
};
preview_window.location.href = pdf_url;
console.log("preview_window.location.href set");
}, 500);
};
};
</script>
</head>
<body>
<button id="preview_button">Show Preview</button>
</body>
</html>
虽然我上面的演示代码避免了它,但我确实为我的应用程序加载了 jQuery,所以如果这能让事情变得更容易,我愿意使用它。
我确实在搜索中找到了 this question,但在那种情况下,主 window ("window") 指向一个新的 URL,而 OP 从来没有在评论中询问如果 window 来自 window.open.
是否有区别时得到回复
正如您所发现的,您无法从开启器中设置 open()
ed windows' onload 事件。
您将不得不在第二页中注入一些脚本来调用其 window.opener
函数。
但是由于您打开的是 pdf 文件,浏览器将完全重新解析您的页面并且您注入的代码将会消失。
正如您在评论中发现的那样,解决方案是将 blob 的 url 注入 iframe,并等待此 iframe 的加载事件。
方法如下:
index.html
<script>
// The callback that our pop-up will call when loaded
function imDone(url){
window.URL.revokeObjectURL(url);
}
var build_blob = function(mime_type, data) {
var buf = new ArrayBuffer(data.length);
var ia = new Uint8Array(buf);
for (var i = 0; i < data.length; i++) ia[i] = data.charCodeAt(i);
var blob = new Blob([ buf ], { type: mime_type });
return blob;
};
var preview_window=null;
window.onload = function(e) {
document.getElementById('preview_button').onclick = function(e) {
if(preview_window===null || preview_window.closed){
// open the window in the onclick handler so we don't trigger popup blocking
preview_window = window.open('html2.html', 'preview_window');
}
// avoid reopening the window since it may cache our last blob
else preview_window.focus();
// use setTimeout to simulate an asynchronous AJAX request
setTimeout(function(e) {
var pdf_data = /* Your pdf data */
var pdf_blob = build_blob('application/pdf', pdf_data);
var pdf_url = window.URL.createObjectURL(pdf_blob);
// Simple loop if our target document is not ready yet
var loopLoad = function(url){
var doc = preview_window.document;
if(doc){
var iframe = doc.querySelector('iframe');
if(iframe)iframe.src = url;
else setTimeout(function(){loopLoad(url);},200);
}
else setTimeout(function(){loopLoad(url);},200)
};
loopLoad(pdf_url);
}, 0);
};
};
</script>
和html2.html
<html>
<head>
<title>Iframe PDF Demo</title>
<style>
body, html, iframe{margin:0; border:0}
</style>
</head>
<body>
<iframe width="100%" height="100%"></iframe>
<script>
document.querySelector('iframe').onload = function(){
//first check that our src is set
if(this.src.indexOf('blob')===0)
// then call index.html's callback
window.opener.imDone(this.src);
}
</script>
</body>
</html>
我有一个网络应用程序。它在 Google Chrome 中运行,不需要在任何其他浏览器中工作。
我有在服务器上生成并在 AJAX 请求中发送回客户端的 PDF 数据。 我从 PDF 数据创建了一个 blob。 我使用 window.URL.createObjectURL 从 blob 创建一个 URL,然后我将其加载到一个 window(我的 preview_window)中,该 window 之前是为显示 PDF 而创建的。
为了加载 URL,我设置了 preview_window.location.href。
我想调用 revokeObjectURL 以避免在 window 中生成和预览新 PDF 时浪费越来越多的资源。 问题是在设置 preview_window.location.href 后立即调用它太早了,并且会停止显示 PDF。所以我只想在 URL 加载后调用 revokeObjectURL 。 为此,我曾尝试将 preview_window.onload 设置为回调,但它从未被调用过。
我想知道:
- 当 window 加载 URL 时是否可以触发回调,正如我正在尝试做的那样?怎么样?
- 是否有其他方法可以确保及时调用 revokeObjectURL?
如果我无法在 window 完成加载 URL 时触发 revokeObjectURL,我可能会在生成新对象之前立即撤销每个 URL。但我宁愿在加载完成后立即撤销 URL,如果可能的话。
我准备了一个 html 文件,它很好地说明了情况:
<html>
<head>
<title>Show PDF Demo</title>
<script>
var build_blob = function(mime_type, data) {
var buf = new ArrayBuffer(data.length);
var ia = new Uint8Array(buf);
for (var i = 0; i < data.length; i++) ia[i] = data.charCodeAt(i);
var blob = new Blob([ buf ], { type: mime_type });
return blob;
};
window.onload = function(e) {
document.getElementById('preview_button').onclick = function(e) {
// open the window in the onclick handler so we don't trigger popup blocking
var preview_window = window.open(null, 'preview_window');
// use setTimeout to simulate an asynchronous AJAX request
setTimeout(function(e) {
var pdf_data = atob(
"JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZwovT3V0bGluZXMgMiAwIFIKL1BhZ2Vz" +
"IDMgMCBSCj4+CmVuZG9iagoyIDAgb2JqCjw8IC9UeXBlIC9PdXRsaW5lcwovQ291bnQgMAo+Pgpl" +
"bmRvYmoKMyAwIG9iago8PCAvVHlwZSAvUGFnZXMKL0tpZHMgWzQgMCBSXQovQ291bnQgMQo+Pgpl" +
"bmRvYmoKNCAwIG9iago8PCAvVHlwZSAvUGFnZQovUGFyZW50IDMgMCBSCi9NZWRpYUJveCBbMCAw" +
"IDUwMCAyMDBdCi9Db250ZW50cyA1IDAgUgovUmVzb3VyY2VzIDw8IC9Qcm9jU2V0IDYgMCBSCi9G" +
"b250IDw8IC9GMSA3IDAgUiA+Pgo+Pgo+PgplbmRvYmoKNSAwIG9iago8PCAvTGVuZ3RoIDczID4+" +
"CnN0cmVhbQpCVAovRjEgMjQgVGYKMTAwIDEwMCBUZAooU01BTEwgVEVTVCBQREYgRklMRSkgVGoK" +
"RVQKZW5kc3RyZWFtCmVuZG9iago2IDAgb2JqClsvUERGIC9UZXh0XQplbmRvYmoKNyAwIG9iago8" +
"PCAvVHlwZSAvRm9udAovU3VidHlwZSAvVHlwZTEKL05hbWUgL0YxCi9CYXNlRm9udCAvSGVsdmV0" +
"aWNhCi9FbmNvZGluZyAvTWFjUm9tYW5FbmNvZGluZwo+PgplbmRvYmoKeHJlZgowIDgKMDAwMDAw" +
"MDAwMCA2NTUzNSBmCjAwMDAwMDAwMDkgMDAwMDAgbgowMDAwMDAwMDc0IDAwMDAwIG4KMDAwMDAw" +
"MDEyMCAwMDAwMCBuCjAwMDAwMDAxNzkgMDAwMDAgbgowMDAwMDAwMzY0IDAwMDAwIG4KMDAwMDAw" +
"MDQ2NiAwMDAwMCBuCjAwMDAwMDA0OTYgMDAwMDAgbgp0cmFpbGVyCjw8IC9TaXplIDgKL1Jvb3Qg" +
"MSAwIFIKPj4Kc3RhcnR4cmVmCjYyNQolJUVPRg=="
);
/*
Warning: for my Chrome (Version 44.0.2403.155 m), the in-built PDF viewer doesn't seem
to work with a blob when this html page is loaded from the local filesystem. I have only
got this to work when fetching this page via HTTP.
*/
var pdf_blob = build_blob('application/pdf', pdf_data);
var pdf_url = window.URL.createObjectURL(pdf_blob);
preview_window.onload = function(e) {
console.log("preview_window.onload called"); // never happens
window.URL.revokeObjectURL(pdf_url);
};
preview_window.location.href = pdf_url;
console.log("preview_window.location.href set");
}, 500);
};
};
</script>
</head>
<body>
<button id="preview_button">Show Preview</button>
</body>
</html>
虽然我上面的演示代码避免了它,但我确实为我的应用程序加载了 jQuery,所以如果这能让事情变得更容易,我愿意使用它。
我确实在搜索中找到了 this question,但在那种情况下,主 window ("window") 指向一个新的 URL,而 OP 从来没有在评论中询问如果 window 来自 window.open.
是否有区别时得到回复正如您所发现的,您无法从开启器中设置 open()
ed windows' onload 事件。
您将不得不在第二页中注入一些脚本来调用其 window.opener
函数。
但是由于您打开的是 pdf 文件,浏览器将完全重新解析您的页面并且您注入的代码将会消失。
正如您在评论中发现的那样,解决方案是将 blob 的 url 注入 iframe,并等待此 iframe 的加载事件。
方法如下:
index.html
<script>
// The callback that our pop-up will call when loaded
function imDone(url){
window.URL.revokeObjectURL(url);
}
var build_blob = function(mime_type, data) {
var buf = new ArrayBuffer(data.length);
var ia = new Uint8Array(buf);
for (var i = 0; i < data.length; i++) ia[i] = data.charCodeAt(i);
var blob = new Blob([ buf ], { type: mime_type });
return blob;
};
var preview_window=null;
window.onload = function(e) {
document.getElementById('preview_button').onclick = function(e) {
if(preview_window===null || preview_window.closed){
// open the window in the onclick handler so we don't trigger popup blocking
preview_window = window.open('html2.html', 'preview_window');
}
// avoid reopening the window since it may cache our last blob
else preview_window.focus();
// use setTimeout to simulate an asynchronous AJAX request
setTimeout(function(e) {
var pdf_data = /* Your pdf data */
var pdf_blob = build_blob('application/pdf', pdf_data);
var pdf_url = window.URL.createObjectURL(pdf_blob);
// Simple loop if our target document is not ready yet
var loopLoad = function(url){
var doc = preview_window.document;
if(doc){
var iframe = doc.querySelector('iframe');
if(iframe)iframe.src = url;
else setTimeout(function(){loopLoad(url);},200);
}
else setTimeout(function(){loopLoad(url);},200)
};
loopLoad(pdf_url);
}, 0);
};
};
</script>
和html2.html
<html>
<head>
<title>Iframe PDF Demo</title>
<style>
body, html, iframe{margin:0; border:0}
</style>
</head>
<body>
<iframe width="100%" height="100%"></iframe>
<script>
document.querySelector('iframe').onload = function(){
//first check that our src is set
if(this.src.indexOf('blob')===0)
// then call index.html's callback
window.opener.imDone(this.src);
}
</script>
</body>
</html>