【问题标题】:File System Access API: is it possible to store the fileHandle of a saved or loaded file for later use?文件系统访问 API:是否可以存储已保存或已加载文件的文件句柄以供以后使用?
【发布时间】:2021-01-27 22:54:11
【问题描述】:

在使用新的(ish)File System Access API 的应用程序上工作,我想保存最近加载的文件的文件句柄,以显示“最近的文件...”菜单选项并让用户加载其中一个文件而不打开系统文件选择窗口。

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

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

除了 JSON 之外,还有其他方法可以序列化句柄吗?我想也许 IndexedDB 会自动完成,但这似乎也不起作用。

【问题讨论】:

    标签: javascript serialization indexeddb


    【解决方案1】:

    这是一个最小示例,演示如何在 IndexedDB 中存储和检索文件句柄(准确地说是 FileSystemHandle)(代码使用 idb-keyval 库为简洁起见):

    import { get, set } from 'https://unpkg.com/idb-keyval@5.0.2/dist/esm/index.js';
    
    const pre = document.querySelector('pre');
    const button = document.querySelector('button');
    
    button.addEventListener('click', async () => {
      try {
        const fileHandleOrUndefined = await get('file');    
        if (fileHandleOrUndefined) {      
          pre.textContent =
              `Retrieved file handle "${fileHandleOrUndefined.name}" from IndexedDB.`;
          return;
        }
        // This always returns an array, but we just need the first entry.
        const [fileHandle] = await window.showOpenFilePicker();
        await set('file', fileHandle);    
        pre.textContent =
            `Stored file handle for "${fileHandle.name}" in IndexedDB.`;
      } catch (error) {
        alert(error.name, error.message);
      }
    });
    

    我创建了一个demo,它显示了上面的代码。

    【讨论】:

    • 谢谢,这很有帮助。还有一个问题:您知道是否可以将句柄存储在 chrome.storage.local 数据库中?还是 IndexedDB 是保留完整对象的唯一方法?
    • 很可能不是,因为它是根据本地存储建模的,它转换为字符串,这意味着它无法序列化文件句柄。
    【解决方案2】:

    当平台接口为[Serializable] 时,表示它具有关联的内部序列化和反序列化规则,这些规则将被执行“结构化克隆”算法的 API 用于创建 JS 的“副本”价值观。如前所述,Structured cloning 由 Message API 使用。 History API 也使用它,因此至少在理论上,您可以将 FileHandle 对象与历史条目关联起来。

    在撰写本文时,在 Chromium 中,FileHandle 对象在与 history.state 一起使用时似乎可以成功序列化和反序列化,例如跨重新加载和向后导航。奇怪的是,当返回到转发条目时,反序列化似乎会默默地失败:popStateEvent.state 和 history.state 在遍历转发到关联状态包含一个或多个 FileHandles 的条目时总是返回null。这似乎是一个错误。

    历史条目是“会话”存储“架子”的一部分。这里的 Session 是指(大致)“选项卡/窗口的生命周期”。这有时可能正是您想要的 FileHandle(例如,在向后遍历时,重新打开在早期状态下打开的文件)。然而,它对跨越多个会话的“原始货架”生命周期存储没有帮助。据我所知,唯一可以为原始级存储序列化和反序列化 FileHandle 的 API 是 IndexedDB。

    【讨论】:

      【解决方案3】:

      对于那些使用Dexie 与 IndexedDB 接口的人,除非您将主键保留为未命名(“非入站”),否则您将获得一个空对象:

      db.version(1).stores({
        test: '++id'
      });
      
      const [fileHandle] = await window.showOpenFilePicker();
      db.test.add({ fileHandle })
      

      这会产生带有{ fileHandle: {} }(空对象)的记录

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

      db.version(1).stores({
        test: '++'
      });
      
      const [fileHandle] = await window.showOpenFilePicker();
      db.test.add({ fileHandle })
      

      结果:{ fileHandle: FileSystemFileHandle... }

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

      【讨论】:

        猜你喜欢
        • 2022-06-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-07-14
        • 2019-10-02
        • 2018-10-24
        • 2011-08-11
        • 2022-01-18
        相关资源
        最近更新 更多