英文:
Why postMessage fails on FileSystemFileHandle?
问题
我有index.html
:
const openButton = document.querySelector('#open'),
pickerOpts = {
types: [{
accept: {
"text/*": [".txt"],
},
}, ],
excludeAcceptAllOption: true,
multiple: false,
}
openButton.addEventListener('click', async() => {
const handle = await window.showOpenFilePicker(pickerOpts),
obj = {
a: 'a'
},
tab = window.open('tab.html')
setTimeout(() => {
// not work, the tab.html receives nothing
tab.postMessage(handle, '*')
// works fine
// tab.postMessage(obj, '*')
}, 1000)
})
和 tab.html
:
window.addEventListener('message', (e) => {
console.log(e.data)
})
postMessage
对于通用的 JavaScript 对象可以正常工作,但无法发送 FileSystemFileHandle
。我该如何修复这个问题?
结构化克隆算法 - Web API | MDN 指出 FileSystemFileHandle
应该是支持的。
英文:
I have index.html
:
<!-- language: lang-js -->
const openButton = document.querySelector('#open'),
pickerOpts = {
types: [{
accept: {
"text/*": [".txt"],
},
}, ],
excludeAcceptAllOption: true,
multiple: false,
}
openButton.addEventListener('click', async() => {
const handle = await window.showOpenFilePicker(pickerOpts),
obj = {
a: 'a'
},
tab = window.open('tab.html')
setTimeout(() => {
// not work, the tab.html receives nothing
tab.postMessage(handle, '*')
// works fine
// tab.postMessage(obj, '*')
}, 1000)
})
<!-- language: lang-html -->
<button id="open">open</button>
<!-- end snippet -->
and tab.html
:
<!-- language: lang-js -->
window.addEventListener('message', (e) => {
console.log(e.data)
})
<!-- end snippet -->
The postMessage
works fine for general JavaScript objects but fails to send a FileSystemFileHandle
. How should I fix this?
The structured clone algorithm - Web APIs | MDN indicates that FileSystemFileHandle
should be supported.
答案1
得分: 1
FileSystemFileHandle
对象确实可以通过 postMessage()
使用的结构化克隆算法进行克隆,但是如果您的两个文档不在相同的源上,当接收端尝试反序列化对象时,此操作应该会引发 DataCloneError
异常。
请注意,file://
系统上的两个文档将被视为跨源。
要处理这种错误,您应该能够在接收端添加一个 messageerror
事件,但这仅在Firefox中适用(在使用 navigator.storage.getDirectory()
时),在Chrome中不适用。
在该浏览器中,除了 data
将为 null
这一事实之外,没有其他信息表明操作失败。但这违反了规范,因此可能被视为错误。
但无论如何,如果您无法访问要发布的窗口,您可以知道它会引发异常,因此这只会对您有一点帮助。
相反,您可能需要设置一个代理API,在这个API中,您的主页面将请求文件系统,然后通过调用 postMessage
让弹出窗口导航到它。我建议您为此目的设置一个唯一的 MessageChannel
,以便您可以将通信封装成 Promise。
然而,重新编写整个API不是一个小项目,因此您可能只需从您确定会使用的少数几种方法开始。
英文:
FileSystemFileHandle
objects can indeed be cloned through the structured cloning algorithm used by postMessage()
, however, if your two documents are not on the same origin, this operation should throw a DataCloneError
exception on the receiving end when it will try to deserialize the object.
Note that two documents on the file://
system will be seen as cross-origin.
To handle such an error, you should be able to add a messageerror
event on the receiving end, however this is the case only in Firefox (when using navigator.storage.getDirectory()
), but not in Chrome.
In that browser, there is thus nothing that tells us that it failed, apart from the fact that data
will be null
. But this is against the specs, so may be considered a bug.
But anyway, you can know that it will throw if you can't access the window you're posting to, so that would help you only a little.
Instead, what you will probably have to do is to set up a proxy API where your main page will request the FileSystem, and then let the popup navigate it through calls to postMessage
. My advice would be to set up a unique MessageChannel
to this effect, so that you can promisify the communication.
However rewriting the whole API is not a small project, so you may to instead start only with the few methods you're sure you'll be using.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论