文件系统访问 API:是否可以存储已保存或已加载文件的文件句柄供以后使用?

File System Access API: is it possible to store the fileHandle of a saved or loaded file for later use?

正在开发一个使用新的(ish)File System Access API 的应用程序,我想保存最近加载文件的文件句柄,以显示“最近的文件...”菜单选项并让用户在不打开系统文件选择的情况下加载这些文件之一 window。

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

File handles are serializable, which means that you can save a file handle to IndexedDB, or call postMessage() to send them between the same top-level origin.

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

这是一个演示如何存储和检索文件句柄的最小示例(为简洁起见,FileSystemHandle to be precise) in IndexedDB (the code uses the 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 来显示上面的代码。

对于那些使用 Dexie 与 IndexedDB 交互的用户,您将得到一个空对象,除非您保留未命名的主键 ('not inbound'):

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

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

在撰写本文时,在 Chromium 中,FileHandle 对象在与 history.state 一起使用时似乎可以成功序列化和反序列化,例如跨重新加载和向后导航。奇怪的是,当 return 指向前向条目时,似乎反序列化可能会悄无声息地失败: popStateEvent.state 和 history.state 总是 return null 当遍历到一个条目时,其关联状态包括一个或多个文件句柄。这似乎是一个错误。

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