17

正在开发使用新(ish)的应用程序文件系统访问API,我想保存最近加载的文件的fileHandles,以显示“Recent files…”菜单选项,并允许用户在不打开系统文件选择窗口的情况下加载其中一个文件。

这篇文章有一段关于在IndexedDB中存储fileHandles的内容,其中提到从API返回的句柄是“可序列化的”,但它没有任何示例代码,JSON.stringify不会这样做。

文件句柄是可序列化的,这意味着您可以将文件句柄保存到IndexedDB,或调用postMessage()在同一顶级源之间发送它们。

除了JSON之外,还有没有序列化句柄的方法?我认为IndexedDB可能会自动完成这项工作,但这似乎也不起作用。

4个答案4

重置为默认值
20

下面是一个演示如何存储和检索文件句柄(文件系统句柄精确地说)在IndexedDB中(代码使用idb-keyval码为了简洁起见,库):

从'导入{get,set}https://unpkg.com/[电子邮件保护]/dist/esm/index.js';const-pre=document.querySelector('pre');const按钮=document.querySelector('按钮');button.addEventListener('click',async()=>{尝试{const fileHandleOrUndefined=等待获取('file');if(fileHandleOrUndefined){前.text内容=`已从IndexedDB.`;检索到文件句柄“${fileHandleOrUndefined.name}”;回报;}//这总是返回一个数组,但我们只需要第一个条目。const[fileHandle]=等待窗口.showOpenFilePicker();等待集('file',fileHandle);前.text内容=`IndexedDB.`;中“${fileHandle.name}”的存储文件句柄;}捕获(错误){警报(error.name,error.message);}});

我已经创建了演示这显示了上述代码的实际操作。

6
  • 谢谢,这很有帮助。还有一个问题:您知道是否可以将句柄存储在chrome.storage.local数据库中吗?还是IndexedDB是保留完整对象的唯一方法? 评论 2021年1月28日18:12
  • 很可能不是,因为它是根据本地存储建模的,本地存储将转换为字符串,这意味着它无法序列化文件句柄。 评论 2021年1月28日22:13
  • DenverCoder9我在我的安卓手机(chrome)上测试了这个演示,但没有成功。你知道我如何在手机上使用Chrome获得同样的效果吗? 评论 2022年9月9日17:30
  • 1
    @JohanEcAv文件系统访问API目前在Android上不受支持。 评论 2022年9月11日9:50
  • 1
    @JohanEcAv我不知道,因为目前根本不存在潜在的概念。 评论 2022年9月13日17:04
8

当平台接口[可序列化],这意味着它与内部的执行“结构化克隆”算法以创建JS值“副本”的API将使用的序列化和反序列化规则。结构化克隆如前所述,由消息API使用。历史API也使用它,因此至少在理论上可以将FileHandle对象与历史条目关联起来。

在撰写本文时,文件句柄与一起使用时,对象似乎已成功序列化和反序列化历史.状态一般来说,例如跨重载和反向导航。奇怪的是,当返回到转发条目时,反序列化似乎会自动失败:popStateEvent.state和history.state总是返回无效的当向前遍历到其关联状态包括一个或多个FileHandles的条目时。这似乎是一个错误。

历史记录条目是“会话”存储“工具架”的一部分。此处的会话(大致)指的是“选项卡/窗口的生存期”。有时,这正是FileHandle所需的内容(例如,向后遍历时,重新打开以前状态下打开的文件)。然而,它对跨多个会话的“源站货架”终身存储没有帮助。据我所知,唯一可以对源级存储的FileHandle进行序列化和反序列化的API是IndexedDB。

1

对于使用德克西要与IndexedDB接口,您将获得一个空对象,除非您未命名主键(“not inbound”):

数据库.version(1).stores({测试:“++id”});const[fileHandle]=等待窗口.showOpenFilePicker();db.test.add({fileHandle})

这将生成一个带有{fileHandle:{}}(空对象)

但是,如果不命名主键,它会正确序列化对象:

数据库.version(1).stores({测试:“++”});const[fileHandle]=等待窗口.showOpenFilePicker();db.test.add({fileHandle})

结果:{fileHandle:FileSystemFileHandle…}

形象

这可能是Dexie中的一个错误,如下所述:https://github.com/dfahlander/Dexie.js/issues/1236

0

我专门为存储和检索文件系统目录句柄

/**@type{Promise<IDBDatabase>}*/const dbPromise=新承诺((resolve,reject)=>{const请求=indexedDB.open('handle',1);request.oupgradeneeded=(事件)=>{const数据库=事件目标结果;db.createObjectStore(“句柄”,{keyPath:“id”});};request.onsuccess=(事件)=>{解决(event.target.result);};request.onerror=(事件)=>{console.error(事件);拒绝(event.target.error);};});/***将目录句柄保存到数据库。*@param{FileSystemDirectoryHandle}dirHandle-要保存的目录句柄。*@returns{Promise<void>}保存句柄时解析的承诺。*/export const saveDirHandle=(dirHandle)=>{return new Promise((resolve,reject)=>{dbPromise.then((db)=>{const事务=db.transaction(['handles'],'readwrite');const store=transaction.objectStore('handles');const请求=store.put({id:'lastFolder',handle:dirHandle});request.onsuccess=()=>resolve();request.onerror=()=>拒绝(request.error);});});};/***从数据库检索上次保存的文件夹句柄。*@return{Promise<FileSystemDirectoryHandle | undefined>}一个用文件夹句柄解析的Promise,如果找不到,则解析为undefined。*/export const getLastFolderHandle=()=>{return new Promise((resolve,reject)=>{dbPromise.then((db)=>{const事务=db.transaction(['handles'],'readonly');const store=transaction.objectStore('handles');const请求=store.get('lastFolder');request.onsuccess=(event)=>解析(event.target.result?.handle);request.onerror=()=>拒绝(request.error);});});};

您的答案

单击“发布您的答案”,表示您同意我们的服务条款并确认您已阅读我们的隐私政策.

不是你想要的答案吗?浏览标记的其他问题问你自己的问题.