文件系统访问API:是否可以存储已保存或加载的文件的fileHandle以备将来使用?

15
我正在开发一个应用程序,使用新的文件系统访问API。我想保存最近加载文件的fileHandles,以便显示“最近文件”菜单选项,并允许用户在不打开系统文件选择窗口的情况下加载其中一个文件。 这篇文章提到了在IndexedDB中存储fileHandles的段落,并且它提到从API返回的句柄是“可序列化的”,但它没有任何示例代码,而JSON.stringify也不能实现序列化。

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

除了JSON之外,是否有其他序列化句柄的方法?我认为IndexedDB可能会自动完成,但似乎也行不通。
3个回答

19

这里是一个最简示例,演示了如何在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);
  }
});

我已经创建了一个演示,展示了上述代码的实际效果。


谢谢,这非常有帮助。还有一个问题:您知道是否可以将句柄存储在chrome.storage.local数据库中吗?或者IndexedDB是唯一能保留完整对象的方法? - Charles Johnson
3
很可能不行,因为它是基于本地存储模型的,该模型转换为字符串,这意味着它无法序列化文件句柄。 - DenverCoder9
DenverCoder9 我在我的安卓手机(使用Chrome浏览器)上测试了这个演示,但是它没有成功。有什么办法可以让我在手机上使用Chrome获得相同的效果吗? - JohanEcAv
1
@JohanEcAv 目前 Android 不支持文件系统访问 API。 - DenverCoder9
@DenverCoder9 还有其他获取它的方式吗?还有其他API或库吗?:( - JohanEcAv
1
目前我所知道的没有,因为简单地说这个概念目前不存在。 - DenverCoder9

8

当一个平台接口被 [Serializable] 标记时,它意味着它具有相关的内部序列化和反序列化规则,这些规则将被执行“结构化克隆”算法的API使用来创建JS值的“副本”。正如提到的那样,结构化克隆被消息API所使用。在理论上,历史API也使用了它,因此你可以将FileHandle对象与历史记录条目相关联以进行持久化。

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

历史记录条目是“会话”存储“架子”的一部分。这里的会话指的是(大致)“选项卡/窗口的生命周期”。对于FileHandle来说,这有时可能正好是你想要的(例如,在向后遍历时,重新打开在早期状态下打开的文件)。但它并不能帮助处理跨多个会话粘滞的“源架子”生命周期存储。就我所知,唯一能够将FileHandle序列化和反序列化以进行源级别存储的API是IndexedDB。


1

对于那些使用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... }

image

这可能是Dexie的一个错误,如此报告:https://github.com/dfahlander/Dexie.js/issues/1236


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接