我想要一个比什么更干净的用户界面
<input type="file" ...>
给我。
我可以使用 window.showOpenFilePicker() 获取用户手势安全上下文并从该 API 获取“fileSystemHandle”,但我不知道如何使用 FileReader 对象读取该文件。 (或任何其他方式)
这是我的失败代码:
/**
* Used to import via a simple button.
* The caller must call window.showOpenFilePicker() and give the
* fileHandle to this API.
* @param {*} fileHandle
* @param {*} fnImportToUI
*/
fio.importFromFileButton = async function(fileHandle, fnImportToUI) {
if (Array.isArray(fileHandle)) {
fileHandle = fileHandle[0];
}
var blob;
const reader = new FileReader();
reader.addEventListener('loadend', (event) => {
blob = new Blob([new Uint8Array(event.target.result)], {type: fileHandle.type })
fnImportToUI(fileHandle, event.target.result)
});
reader.readAsArrayBuffer(blob);
}
问题在于 fileSystemHandle 不是 Blob(如果我调用,我会收到错误
reader.readAsArrayBuffer(fileHandle);
or
reader.readAsText(fileHandle);
或使用任何 ReadFile 读取 API。)
据我所知,在读取文件以获取数据之前,我无法将 fileSystemHandle 转换为 Blob - 这是先有鸡还是先有蛋的问题。
我不确定该代码来自哪里,但使用该 API 的正确方法如下所示。假设您有一个文件
foo.txt
,其内容为“Hello, world!”其中:
// Returns a Promise of a single File object
async function pickSingleFile() {
const [fileHandle] = await window.showOpenFilePicker();
return await fileHandle.getFile();
}
// Takes a File object, returns a Promise of a string
async function readFile(file) {
return await file.text();
}
pickSingleFile() // user picks foo.txt
.then(readFile)
.then(console.log) // prints "Hello, world!"
.catch(console.error); // in case of oops
您实际上可以在此页面上进行测试,方法是创建一个文本文件,将该代码粘贴到浏览器控制台(假设您使用的是桌面 Chrome 或 Edge)并导航到打开的文件选择器中的正确文件。
请注意,正如我在评论中所说,此功能在撰写本文时支持很糟糕并且仅适用于基于 Chromium 的浏览器的桌面版本,不适用于 Safari,不适用于 FireFox,也不适用于移动设备(甚至不适用于 Android 上的 Chrome) ,所以使用前请仔细考虑。
您可能会发现 Medium 上的这篇文章非常有用:“探索 JavaScript 中的高级文件读取:释放 showOpenFilePicker 的力量”。它深入研究了 showOpenFilePicker API,它提供了一种在 JavaScript 中读取文件的创新方法,而无需依赖传统的输入字段。本文解释了它的好处,演示了代码片段,甚至讨论了现实世界的用例。在探索这个主题时,我发现它是一个很好的资源。查看该技术的详细探索!
您的代码片段中有几个错误,我已根据我的假设进行了修复。代码几乎是不言自明的。
/**
* convert a File object into base64 dataurl or plain text, returns null if its aborted
* @param {File} file
* @returns {Promise<string|null>}
* @param {'plain'|'base64'} mode
*/
const convertFileToDataURLOrString = (file, mode) =>
new Promise((resolve, reject) => {
const reader = new FileReader()
reader.addEventListener('load', () => resolve('' + reader.result))
reader.addEventListener('abort', () => resolve(null))
reader.addEventListener('error', () => reject(reader.error))
if (mode === 'plain') reader.readAsText(file)
else reader.readAsDataURL(file)
})
fio.importFromFileButton = async function (fileHandle, fnImportToUI) {
if (Array.isArray(fileHandle)) {
fileHandle = fileHandle[0]
}
const datastr = convertFileToDataURLOrString(fileHandle, 'plain')
// do something with datastr
}