文件系统(File System)

现行标准 — 最后更新

参与:
GitHub whatwg/fs新建议案开放建议案
Matrix 聊天
提交历史:
GitHub whatwg/fs/commits
截至此提交的快照
@whatfilesystem
测试:
web-platform-tests fs/持续完善中
翻译 (非规范性)
日本語
简体中文

摘要(Abstract)

文件系统(File System)定义了文件系统的基础结构及其 API。

1. 引言

本节为非规范性内容。

本文件定义了文件系统 API 的基础架构。此外,还定义了一个 API,使网站可以在不先请求用户授权的情况下,访问文件系统目录。这使得网站能够在用户选择保存位置之前,将数据保存到磁盘,而无需为此类文件使用完全不同的存储机制和 API。入口为 navigator.storage.getDirectory() 方法。

2. 文件与目录

2.1. 基本概念

文件系统条目是指文件条目或目录条目。

每个文件系统条目都关联有一个查询访问算法,接收“read”或“readwrite模式,返回一个文件系统访问结果。除非另有说明,否则该算法返回的文件系统访问结果的权限状态为“denied”,错误名称为空字符串。

每个文件系统条目还关联有一个请求访问算法,参数与查询访问相同,返回的文件系统访问结果类似,除非另有说明,否则权限状态为“denied”,错误名称为空字符串。

文件系统访问结果是一个结构体,封装了查询或请求文件系统访问的结果。它有如下字段:

权限状态

一个 PermissionState

错误名称

一个字符串。如果权限状态为“granted”,则必须为空字符串;否则应为 DOMException 错误名称表中的名称。大多数情况下,权限未“granted”时,名称应为“NotAllowedError”。

相关规范可将此 API 视为强功能。然而,与其他强功能的权限请求算法可能抛出异常不同,文件系统条目的“查询访问”和“请求访问”算法必须在文件系统队列中并行运行,不允许抛出异常。如果算法返回的错误名称非空,调用者应通过队列存储任务来拒绝(reject)。

注意: 仅实现本规范而未实现其依赖规范的实现,无需实现文件系统条目的“查询访问”和“请求访问”算法。

应考虑将访问检查算法关联到 FileSystemHandle。

每个文件系统条目有一个关联的名称(字符串)。

有效文件名指非空字符串,不等于“.”或“..”,且不包含“/”或底层平台作为路径分隔符的任何其他字符。

注意: 这意味着在 Windows 上名称中不能包含“\”,但在其他操作系统可能允许。此外,底层文件系统可能对名称有进一步限制,仅为有效文件名并不保证能成功创建该文件或目录。

应考虑对文件名施加更严格的规范性限制,而不是完全交由底层文件系统决定。

文件条目还包括二进制数据(字节序列)、修改时间戳(自 Unix 纪元以来的毫秒数)、(仅允许为“open”“taken-exclusive”“taken-shared”之一的字符串)和共享锁数量(当前持有的共享锁数量)。

用户代理拥有一个关联的文件系统队列,即新建的并行队列。所有文件系统操作都应使用此队列。

获取给定文件条目的锁,value为“exclusive”或“shared”:
  1. lock 为该文件的锁值。
  2. count 为该文件的共享锁数量。
  3. value 为“exclusive”:
    1. lock 为“open”:
      1. 将 lock 设为“taken-exclusive”。
      2. 返回“success”。
  4. value 为“shared”:
    1. lock 为“open”:
      1. 将 lock 设为“taken-shared”。
      2. 将 count 设为 1。
      3. 返回“success”。
    2. 否则,若 lock 为“taken-shared”:
      1. 将 count 加 1。
      2. 返回“success”。
  5. 返回“failure”。

注意:这些步骤必须在文件系统队列中执行。

释放给定文件条目的锁:
  1. lock 为该文件的锁值。
  2. count 为该文件的共享锁数量。
  3. lock 为“taken-shared”:
    1. 将 count 减 1。
    2. 若 count 为 0,将 lock 设为“open”。
  4. 否则,将 lock 设为“open”。

注意:这些步骤必须在文件系统队列中执行。

注意:锁用于防止文件的并发修改。FileSystemWritableFileStream 需要共享锁,FileSystemSyncAccessHandle 需要独占锁。

目录条目还包含一个子集(子条目),每个子项都是文件系统条目,可以是文件条目或目录条目。

文件系统条目 entry 应至多被一个目录条目的子条目包含,该目录条目被称为 entry父项。若不存在这样的目录条目,则其父项为 null。

注意:不同的文件系统条目可表示磁盘上的同一文件或目录,因此两者可拥有不同父项,或一者有父项而另一者无。

文件系统条目可以(但不必须)由宿主操作系统的本地文件系统上的实际文件支持,因此条目的二进制数据、修改时间戳、子条目等可能被此规范之外的应用修改。外部变化如何反映到此规范的数据结构,以及反之,留给具体实现决定。

文件系统条目 a若等于 b,或二者由本地文件系统上的同一文件或目录支持,则为同一条目

解析给定 child 相对于 root 的路径:

  1. result 为新建的 promise。
  2. 将以下步骤加入文件系统队列:
    1. child 的 locator 的 root 不等于 root 的 locator 的 root,则 resolve result 为 null,终止。
    2. childPathchild 的 locator 的 path。
    3. rootPathroot 的 locator 的 path。
    4. childPathrootPath 为同一路径,则 resolve result 为空列表,终止。
    5. rootPath 的长度大于 childPath,则 resolve result 为 null,终止。
    6. 对于 rootPath 的每个索引 index:若 rootPath[index] 不等于 childPath[index],则 resolve result 为 null,终止。
    7. relativePath 为空列表。
    8. 对于 index 从 rootPath 长度到 childPath 长度(不含),将 childPath[index] 添加到 relativePath
    9. resolve resultrelativePath
  3. 返回 result

文件系统定位器表示文件系统条目的一个可能位置。文件系统定位器可以是文件定位器或目录定位器。

每个文件系统定位器有一个关联的路径(文件系统路径),类型(FileSystemHandleKind),以及(文件系统根)。

可考虑为每个定位器分配存储桶。

文件定位器是类型为“file”的文件系统定位器。目录定位器是类型为“directory”的文件系统定位器。

文件系统根是一个实现自定义的字符串。

假设一个文件系统定位器 locator 定位到存储桶文件系统根目录下路径为 data/drafts/example.txt 的文件条目,locator 的类型为“file”,路径为 [ "data", "drafts", "example.txt" ],根可包含例如存储桶、磁盘驱动器等相关信息。

如果 ab 的类型、根和路径均相同,则为同一个定位器

定位条目算法,给定一个文件系统定位器 locator,执行如下实现自定义步骤,但需满足:

获取定位器算法,给定文件系统条目 entry,执行如下实现自定义步骤,但需满足:

文件系统路径是由一个或多个字符串组成的列表。可以是映射到磁盘实际位置或内存的虚拟路径,也可以直接对应本地文件系统路径,或根本不对应任何磁盘文件。其实际物理位置由实现决定。

path 为 [ "data", "drafts", "example.txt" ]。不要求磁盘上真的存在名为 example.txt 的文件。

ab 长度一致,且每个索引的元素均相等,则为同一路径

文件系统定位器的内容(包括路径)不应全部暴露给网站进程。只有当定位器后续被相对于父目录定位器“解析”时,网站才可能知晓路径组件。

2.2. FileSystemHandle 接口

enum FileSystemHandleKind {
  "file",
  "directory",
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemHandle {
  readonly attribute FileSystemHandleKind kind;
  readonly attribute USVString name;

  Promise<boolean> isSameEntry(FileSystemHandle other);
};

FileSystemHandle 对象关联有一个 定位器(即文件系统定位器)。

注意:多个 FileSystemHandle 对象可以具有相同的文件系统定位器。

FileSystemHandle 的定位器的路径第一个元素为空字符串时,该对象位于 bucket 文件系统中

注意: 这是一个特殊约定,仅有 bucket 文件系统的根目录路径才会包含空字符串,见 getDirectory()。路径的其它元素必须是有效文件名。

可考虑为每个定位器分配存储桶以优化此设计。

FileSystemHandle 对象是可序列化对象。

它们的序列化步骤如下,给定 valueserializedforStorage

  1. serialized.[[Origin]] 设为 value 的相关设置对象的源(origin)。
  2. serialized.[[Locator]] 设为 value 的定位器。

它们的反序列化步骤如下,给定 serializedvalue

  1. 如果 serialized.[[Origin]] 与 value 的相关设置对象的源(origin)不同源,则抛出 "DataCloneError" 异常。
  2. value 的定位器设为 serialized.[[Locator]]。
handle . kind

如果 handleFileSystemFileHandle,则返回 "file";如果是 FileSystemDirectoryHandle,则返回 "directory"。可用于区分目录内容遍历时的文件与目录。

handle . name

返回 handle 定位器路径的最后一个部分。

kind 的获取步骤:返回 this 的定位器的类型。

name 的获取步骤:返回 this 的定位器路径的最后一个元素(字符串)。

2.2.1.isSameEntry() 方法

same = await handle1 . isSameEntry(handle2)

如果 handle1handle2 表示同一个文件或目录,则返回 true。

isSameEntry(other) 方法的实现步骤如下:
  1. realmthis 的相关 Realm。
  2. p 为在 realm 中新建的 promise。
  3. 将以下步骤加入文件系统队列:
    1. 如果 this 的定位器与 other 的定位器为同一个定位器,resolve p 为 true。
    2. 否则,resolve p 为 false。
  4. 返回 p

2.3. FileSystemFileHandle 接口

dictionary FileSystemCreateWritableOptions {
  boolean keepExistingData = false;
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemFileHandle : FileSystemHandle {
  Promise<File> getFile();
  Promise<FileSystemWritableFileStream> createWritable(optional FileSystemCreateWritableOptions options = {});
  [Exposed=DedicatedWorker]
  Promise<FileSystemSyncAccessHandle> createSyncAccessHandle();
};

注意: FileSystemFileHandle 的定位器类型为 "file"。

创建子 FileSystemFileHandle,给定目录定位器 parentLocator 和字符串 name(在 Realm realm 中):
  1. 创建 handle,为 realm 中新的 FileSystemFileHandle
  2. childType 为 "file"。
  3. childRootparentLocator 的根的副本。
  4. childPathparentLocator 路径的克隆,并追加 name
  5. handle 的定位器设为:类型为 childType,根为 childRoot,路径为 childPath 的文件系统定位器。
  6. 返回 handle
创建新的 FileSystemFileHandle,给定文件系统根 root 和文件系统路径 path(在 Realm realm 中):
  1. 创建 handle,为 realm 中新的 FileSystemFileHandle
  2. handle 的定位器设为:类型为 "file",根为 root,路径为 path 的文件系统定位器。
  3. 返回 handle

FileSystemFileHandle 对象是可序列化对象,其序列化和反序列化步骤与 FileSystemHandle 相同。

2.3.1.getFile() 方法

file = await fileHandle . getFile()

返回一个 File,表示 handle 的定位器所定位的文件条目在磁盘上的当前状态。如果该文件在调用后被更改或删除,返回的 File 对象可能无法再读取。

getFile() 方法的实现步骤如下:
  1. result 为新的 promise。
  2. locatorthis 的定位器。
  3. globalthis 的相关全局对象。
  4. 将以下步骤加入文件系统队列:
    1. entry 为用 locator 定位的条目。
    2. accessResultentry 的“查询访问”算法结果,模式为 "read"。
    3. 队列一个存储任务(绑定 global)以执行以下步骤:
      1. 如果 accessResult 的权限状态不是 "granted",则以其错误名为参数 reject result,类型为 DOMException,并终止。
      2. 如果 entry 为 null,则以 "NotFoundError" DOMException reject result,并终止。
      3. 断言 entry 是文件条目。
      4. 创建新的 File 对象 f
      5. f 的快照状态设为 entry 当前状态。
      6. f 的底层字节序列设为 entry 的二进制数据的副本。
      7. f 的 name 设为 entry 的名称。
      8. f 的 lastModified 设为 entry 的修改时间戳。
      9. f 的 type 设为实现自定义的值(例如基于 entry 的名称或扩展名)。
      10. resolve resultf
  5. 返回 result

2.3.2. createWritable() 方法

stream = await fileHandle . createWritable()
stream = await fileHandle . createWritable({keepExistingData: true/false })

返回一个 FileSystemWritableFileStream,可用于写入文件。通过 stream 所做的更改,只有在流关闭后才会反映到 fileHandle 的定位器所定位的文件条目中。用户代理会尽量确保不会发生部分写入,即文件要么保持原内容,要么包含流关闭前写入的所有数据。

通常实现方式为先将数据写入临时文件,只有在写入流关闭时才用临时文件替换原文件条目。

如果 keepExistingData 为 false 或未指定,则临时文件从空开始,否则会先复制原文件内容到临时文件。

创建 FileSystemWritableFileStream 时会对目标文件条目获取共享锁,这会阻止在流关闭前为该条目创建 FileSystemSyncAccessHandle

关于 createWritable "inPlace" 模式的讨论,见 WICG/file-system-access issue #67。目前 Chrome 尚未实现该功能,因与恶意软件检测等机制冲突。对于 bucket 文件系统,可用 FileSystemSyncAccessHandle 接口实现原地写入。

createWritable(options) 方法实现步骤如下:
  1. result 为新的 promise。
  2. locatorthis 的定位器。
  3. realmthis 的相关 Realm。
  4. globalthis 的相关全局对象。
  5. 将以下步骤加入文件系统队列:
    1. entry 为用 locator 定位的条目。
    2. accessResultentry 的“请求访问”算法结果,模式为 "readwrite"。
    3. 如果 accessResult 权限状态不是 "granted",则队列存储任务(global)reject result,异常类型为 accessResult 的错误名。
    4. 如果 entry 为 null,则队列存储任务(global)reject result,异常类型为 "NotFoundError"。
    5. 断言 entry 是文件条目。
    6. lockResult 为对 entry 获取“共享”锁的结果。
    7. 队列存储任务(global)执行:
      1. 如果 lockResult 为 "failure",则 reject result,异常类型为 "NoModificationAllowedError"。
      2. 创建 stream,即为 entry 创建新的 FileSystemWritableFileStream(在 realm 中)。
      3. 如果 options["keepExistingData"] 为 true,则将 stream 的 [[buffer]] 设为 entry 二进制数据的副本。
      4. resolve resultstream
  6. 返回 result

2.3.3. createSyncAccessHandle() 方法

handle = await fileHandle . createSyncAccessHandle()

返回一个 FileSystemSyncAccessHandle,可用于同步读写文件。通过 handle 所做的更改可能会立即反映到 fileHandle 的定位器所定位的文件条目中。可调用 flush 方法确保更改被写入文件。

创建 FileSystemSyncAccessHandle 时会对目标文件条目获取独占锁,在句柄关闭前,无法再为该条目创建 FileSystemSyncAccessHandleFileSystemWritableFileStream

返回的 FileSystemSyncAccessHandle 提供同步方法,适合如 WebAssembly 这类异步开销较大的场景。

目前,仅当 fileHandle 位于 bucket 文件系统时,该方法才会成功。

createSyncAccessHandle() 方法的实现步骤如下:
  1. result 为新的 promise。
  2. locatorthis 的定位器。
  3. realmthis 的相关 Realm。
  4. globalthis 的相关全局对象。
  5. isInABucketFileSystem,若 this 位于 bucket 文件系统,则为 true,否则为 false。
  6. 将以下步骤加入文件系统队列:
    1. entry 为用 locator 定位的条目。
    2. accessResultentry 的“请求访问”算法结果,模式为 "readwrite"。
    3. 如果 accessResult 权限状态不是 "granted",则队列存储任务(global)reject result,异常类型为 accessResult 的错误名。
    4. 如果 isInABucketFileSystem 为 false,则队列存储任务(global)reject result,异常类型为 "InvalidStateError"。
    5. 如果 entry 为 null,则队列存储任务(global)reject result,异常类型为 "NotFoundError"。
    6. 断言 entry 是文件条目。
    7. lockResult 为对 entry 获取“独占”锁的结果。
    8. 队列存储任务(global)执行:
      1. 如果 lockResult 为 "failure",则 reject result,异常类型为 "NoModificationAllowedError"。
      2. 创建 handle,即为 entry 创建新的 FileSystemSyncAccessHandle(在 realm 中)。
      3. resolve resulthandle
  7. 返回 result

2.4. FileSystemDirectoryHandle 接口

dictionary FileSystemGetFileOptions {
  boolean create = false;
};

dictionary FileSystemGetDirectoryOptions {
  boolean create = false;
};

dictionary FileSystemRemoveOptions {
  boolean recursive = false;
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemDirectoryHandle : FileSystemHandle {
  async iterable<USVString, FileSystemHandle>;

  Promise<FileSystemFileHandle> getFileHandle(USVString name, optional FileSystemGetFileOptions options = {});
  Promise<FileSystemDirectoryHandle> getDirectoryHandle(USVString name, optional FileSystemGetDirectoryOptions options = {});

  Promise<undefined> removeEntry(USVString name, optional FileSystemRemoveOptions options = {});

  Promise<sequence<USVString>?> resolve(FileSystemHandle possibleDescendant);
};

注意: FileSystemDirectoryHandle 的定位器类型为 "directory"。

创建子 FileSystemDirectoryHandle,给定目录定位器 parentLocator 和字符串 name(在 Realm realm 中):
  1. 创建 handle,为 realm 中新的 FileSystemDirectoryHandle
  2. childType 为 "directory"。
  3. childRootparentLocator 的根的副本。
  4. childPathparentLocator 路径的克隆,并追加 name
  5. handle 的定位器设为:类型为 childType,根为 childRoot,路径为 childPath 的文件系统定位器。
  6. 返回 handle
创建新的 FileSystemDirectoryHandle,给定文件系统根 root 和文件系统路径 path(在 Realm realm 中):
  1. 创建 handle,为 realm 中新的 FileSystemDirectoryHandle
  2. handle 的定位器设为:类型为 "directory",根为 root,路径为 path 的文件系统定位器。
  3. 返回 handle

FileSystemDirectoryHandle 对象是可序列化对象,其序列化和反序列化步骤与 FileSystemHandle 相同。

2.4.1. 目录遍历

for await (let [name, handle] of directoryHandle) {}
for await (let [name, handle] of directoryHandle . entries()) {}
for await (let handle of directoryHandle . values()) {}
for await (let name of directoryHandle . keys()) {}

遍历所有父项为 directoryHandle 的定位器所定位的目录条目的子项。遍历过程中创建或删除的条目可能会被包含或不被包含,不做保证。

未来可能会为异步可迭代器声明添加参数,例如递归遍历。

异步迭代器初始化步骤,针对 FileSystemDirectoryHandle handle 及其异步迭代器 iterator
  1. iterator 的“已访问结果”设为空集合。
要为 FileSystemDirectoryHandle handle 及其异步迭代器 iterator 获取下一个迭代结果,步骤如下:
  1. promise新建的 promise

  2. 将以下步骤入队文件系统队列

    1. directory定位条目(用 handlelocator)的结果。

    2. accessResultdirectory查询访问(参数为 "read")的结果。

    3. 入队存储任务(在 handle相关全局对象 上)以执行以下步骤:

      1. 如果 accessResult权限状态不是 "granted",则拒绝 promise,错误为 accessResulterror nameDOMException,终止这些步骤。

      2. 如果 directorynull拒绝 promise,错误为 "NotFoundError" 的 DOMException,终止这些步骤。

        1. 断言directory目录条目

      3. childdirectorychildren 中不在 iteratorpast results 中的、名字为 name文件系统条目;如果不存在则为 null

        注意: 此处有意未指定遍历顺序,不同平台文件系统实现和返回顺序不同,规范不保证遍历顺序。

      4. 如果 childnull,则 resolve promise,值为 undefined,终止这些步骤。

      5. 追加 childnameiteratorpast results

      6. 如果 child文件条目

        1. result创建子 FileSystemFileHandle 的结果,参数为 handlelocatorchildname,作用域为 handle相关 Realm

      7. 否则:

        1. result创建子 FileSystemDirectoryHandle 的结果,参数为 handlelocatorchildname,作用域为 handle相关 Realm

      8. resolve promise,值为 (childname, result)。

  3. 返回 promise

2.4.2. getFileHandle() 方法

fileHandle = await directoryHandle . getFileHandle(name)
fileHandle = await directoryHandle . getFileHandle(name, { create: false })

返回 directoryHandle 的定位器所定位的目录条目下名为 name 的文件句柄。如果文件不存在,则 promise 拒绝。

fileHandle = await directoryHandle . getFileHandle(name, { create: true })

返回 directoryHandle 的定位器所定位的目录条目下名为 name 的文件句柄。如果文件不存在则新建文件,如果无法创建则 promise 拒绝(如同名目录已存在、文件名非法,或出于安全原因禁止创建)。

此操作需要写权限,即使目标文件已存在。如果当前句柄无写权限,可能会弹出权限请求提示。若只需只读访问且无需写权限,可调用 { create: false }

getFileHandle(name, options) 方法实现步骤如下:
  1. result 为新的 promise。
  2. realmthis 的相关 Realm。
  3. locatorthis 的定位器。
  4. globalthis 的相关全局对象。
  5. 将以下步骤加入文件系统队列:
    1. name 不是有效文件名,则队列存储任务(global)reject result,异常类型为 TypeError,并终止。
    2. entry 为用 locator 定位的条目。
    3. options["create"] 为 true,则:
      1. accessResultentry 的“请求访问”算法结果(模式 "readwrite")。
    4. 否则:
      1. accessResultentry 的“查询访问”算法结果(模式 "read")。
    5. 队列一个存储任务(global)以执行:
      1. 如果 accessResult 权限状态不是 "granted",则 reject result,异常类型为 accessResult 的错误名,并终止。
      2. 如果 entry 为 null,则 reject result,异常类型为 NotFoundError,并终止。
      3. 断言 entry 是目录条目。
      4. 遍历 entry 的子项:
        1. child 的 name 等于 name
          1. child 是目录条目,则 reject result,异常类型为 TypeMismatchError,并终止。
          2. resolve result 为用 locatorchild 的 name 在 realm 下创建的子 FileSystemFileHandle,并终止。
      5. options["create"] 为 false,则 reject result,异常类型为 NotFoundError,并终止。
      6. 新建 child,类型为文件条目,其“查询访问”与“请求访问”算法继承自 entry
      7. child 的 name 设为 name
      8. child 的二进制数据设为空字节序列。
      9. child 的修改时间戳设为当前时间。
      10. child 添加到 entry 的子项集合中。
      11. 如果底层文件系统创建 child 时抛出异常,则 reject result 并终止。
      12. resolve result 为用 locatorchild 的 name 在 realm 下创建的子 FileSystemFileHandle
  6. 返回 result

2.4.3. getDirectoryHandle() 方法

subdirHandle = await directoryHandle . getDirectoryHandle(name)
subdirHandle = await directoryHandle . getDirectoryHandle(name, { create: false })

返回 directoryHandle 的定位器所定位的目录条目下名为 name 的子目录句柄。如果该目录不存在,则 promise 拒绝。

subdirHandle = await directoryHandle . getDirectoryHandle(name, { create: true })

返回 directoryHandle 的定位器所定位的目录条目下名为 name 的子目录句柄。如果该目录不存在则新建目录,若无法创建(如同名文件已存在或名称非法)则 promise 拒绝。

此操作需要写权限,即使目标目录已存在。如果当前句柄无写权限,可能会弹出权限请求提示。若仅需只读访问可调用 { create: false }

getDirectoryHandle(name, options) 方法实现步骤如下:
  1. result 为新的 promise。
  2. realmthis 的相关 Realm。
  3. locatorthis 的定位器。
  4. globalthis 的相关全局对象。
  5. 将以下步骤加入文件系统队列:
    1. name 不是有效文件名,则队列存储任务(global)reject result,异常类型为 TypeError,并终止。
    2. entry 为用 locator 定位的条目。
    3. options["create"] 为 true,则:
      1. accessResultentry 的“请求访问”算法结果(模式 "readwrite")。
    4. 否则:
      1. accessResultentry 的“查询访问”算法结果(模式 "read")。
    5. 队列一个存储任务(global)以执行:
      1. 如果 accessResult 权限状态不是 "granted",则 reject result,异常类型为 accessResult 的错误名,并终止。
      2. 如果 entry 为 null,则 reject result,异常类型为 NotFoundError,并终止。
      3. 断言 entry 是目录条目。
      4. 遍历 entry 的子项:
        1. child 的 name 等于 name
          1. child 是文件条目,则 reject result,异常类型为 TypeMismatchError,并终止。
          2. resolve result 为用 locatorchild 的 name 在 realm 下创建的子 FileSystemDirectoryHandle,并终止。
      5. options["create"] 为 false,则 reject result,异常类型为 NotFoundError,并终止。
      6. 新建 child,类型为目录条目,其“查询访问”与“请求访问”算法继承自 entry
      7. child 的 name 设为 name
      8. child 的 children 设为空集合。
      9. child 添加到 entry 的子项集合中。
      10. 如果底层文件系统创建 child 时抛出异常,则 reject result 并终止。
      11. resolve result 为用 locatorchild 的 name 在 realm 下创建的子 FileSystemDirectoryHandle
  6. 返回 result

2.4.4. removeEntry() 方法

await directoryHandle . removeEntry(name)
await directoryHandle . removeEntry(name, { recursive: false })

如果 directoryHandle 的定位器所定位的目录条目包含名为 name 的文件,或空目录,则尝试删除该文件或目录。

删除不存在的文件或目录视为成功;尝试删除非空目录会导致 promise 拒绝。

await directoryHandle . removeEntry(name, { recursive: true })

删除 directoryHandle 的定位器所定位的目录条目下名为 name 的文件系统条目。如果该条目为目录,将递归删除其所有内容。

删除不存在的文件或目录视为成功。

removeEntry(name, options) 方法实现步骤如下:
  1. result 为新的 promise。
  2. locatorthis 的定位器。
  3. globalthis 的相关全局对象。
  4. 将以下步骤加入文件系统队列:
    1. name 不是有效文件名,则队列存储任务(global)reject result,异常类型为 TypeError,并终止。
    2. entry 为用 locator 定位的条目。
    3. accessResultentry 的“请求访问”算法结果(模式 "readwrite")。
    4. 队列一个存储任务(global)以执行:
      1. 如果 accessResult 权限状态不是 "granted",则 reject result,异常类型为 accessResult 的错误名,并终止。
      2. 如果 entry 为 null,则 reject result,异常类型为 NotFoundError,并终止。
      3. 断言 entry 是目录条目。
      4. 遍历 entry 的子项:
        1. child 的 name 等于 name
          1. 如果 child 是目录条目:
            1. 如果 child 的 children 不为空且 options["recursive"] 为 false,则 reject result,异常类型为 InvalidModificationError,并终止。
          2. entry 的 children 中移除 child
          3. 如果底层文件系统移除 child 时抛出异常,则 reject result 并终止。
          4. resolve result 为 undefined。
      5. 若未找到 name,则 reject result,异常类型为 NotFoundError。
  5. 返回 result

2.4.5. resolve() 方法

path = await directory . resolve(child)

如果 child 等于 directory,则 path 是空数组。

如果 childdirectory 的直接子项,则 path 是包含 child 名称的数组。

如果 childdirectory 的后代,则 path 是包含所有中间目录及 child 名称(作为最后一个元素)的数组。例如,若 directory 表示 /home/user/projectchild 表示 /home/user/project/foo/bar,则返回 ['foo', 'bar']

否则(directorychild 无父子关系),path 为 null。

// 假设已获得有效目录句柄。
const dir_ref = current_project_dir;
if (!dir_ref) return;

// 获取文件句柄:
const file_ref = await dir_ref.getFileHandle(filename, { create: true });

// 检查 file_ref 是否位于 dir_ref 内:
const relative_path = await dir_ref.resolve(file_ref);
if (relative_path === null) {
    // 不在 dir_ref 内。
} else {
    // relative_path 是名称数组,表示从 dir_ref 到 file_ref 的相对路径:
    assert relative_path.pop() === file_ref.name;

    let entry = dir_ref;
    for (const name of relative_path) {
        entry = await entry.getDirectory(name);
    }
    entry = await entry.getFile(file_ref.name);

    // 此时 entry 与 file_ref 对应同一磁盘文件。
    assert await entry.isSameEntry(file_ref) === true;
}
resolve(possibleDescendant) 方法的实现步骤是:返回将 possibleDescendant 的定位器相对于 this 的定位器进行 路径解析 的结果。

2.5. FileSystemWritableFileStream 接口

enum WriteCommandType {
  "write",
  "seek",
  "truncate",
};

dictionary WriteParams {
  required WriteCommandType type;
  unsigned long long? size;
  unsigned long long? position;
  (BufferSource or Blob or USVString)? data;
};

typedef (BufferSource or Blob or USVString or WriteParams) FileSystemWriteChunkType;

[Exposed=(Window,Worker), SecureContext]
interface FileSystemWritableFileStream : WritableStream {
  Promise<undefined> write(FileSystemWriteChunkType data);
  Promise<undefined> seek(unsigned long long position);
  Promise<undefined> truncate(unsigned long long size);
};

FileSystemWritableFileStream 具有关联的 [[file]](一个 文件条目)。

FileSystemWritableFileStream 具有关联的 [[buffer]](一个 字节序列),初始为空。

注意:该缓冲区可能变得非常大,因此实现时通常不会将其全部保存在内存中,而是使用临时文件。所有对 [[buffer]] 的访问都通过返回 Promise 的方法和算法完成,因此尽管这些操作看起来是同步的,实际实现可为异步。

FileSystemWritableFileStream 具有关联的 [[seekOffset]](一个数字),初始值为 0。

FileSystemWritableFileStream 对象是 WritableStream 对象的扩展,带有便捷方法并操作单个磁盘文件。

创建后,会有一个底层 sink 可用,流对象立即可用。所有操作都是可排队的,生产者能够响应背压。

底层 sink 的 write 方法(即 WritableStreamDefaultWriter’s write())可接受字节类型数据或 WriteParams 作为输入。

FileSystemWritableFileStream 有一个文件位置游标,初始为文件头部偏移 0 字节。 使用 write() 或通过 WritableStream 能力(即 WritableStreamDefaultWriter’s write()),该位置会根据写入的字节数推进。

同样,将 ReadableStream 管道到 FileSystemWritableFileStream 时,该位置也会根据流经的字节数更新。

getWriter() 返回 WritableStreamDefaultWriter 实例。

创建新的 FileSystemWritableFileStream,给定 文件条目 fileRealm realm,步骤如下:
  1. 创建 stream,为 realm 内新的 FileSystemWritableFileStream
  2. stream 的 [[file]] 设为 file
  3. 定义 writeAlgorithm,其参数为 chunk,返回以 streamchunk 运行“写入块”算法的结果。
  4. 定义 closeAlgorithm,其步骤如下:
    1. closeResult 为新的 promise。
    2. 将以下步骤加入文件系统队列:
      1. accessResultfile 的“查询访问”算法结果,模式为 "readwrite"。
      2. 队列一个存储任务(file 的全局对象)执行:
        1. 如果 accessResult 权限状态不是 "granted",则 reject closeResult,异常类型为 accessResult 的错误名,并终止。
        2. 执行实现自定义的恶意软件和安全检查。若检查失败,reject closeResult,异常类型为 AbortError,并终止。
        3. stream 的 [[file]] 的二进制数据设为 stream 的 [[buffer]]。如抛异常则 reject closeResult 并终止。注:预期原子性更新磁盘文件内容。
        4. 将以下步骤加入文件系统队列:
          1. 释放 stream 的 [[file]] 锁。
          2. 队列存储任务(file 的全局对象),resolve closeResult 为 undefined。
    3. 返回 closeResult
  5. 定义 abortAlgorithm,其步骤如下:
    1. 将以下步骤加入文件系统队列:
      1. 释放 stream 的 [[file]] 锁。
  6. highWaterMark 为 1。
  7. 定义 sizeAlgorithm,返回 1。
  8. 按如下方式设置 stream: writeAlgorithm 设为 writeAlgorithm,closeAlgorithm 设为 closeAlgorithm,abortAlgorithm 设为 abortAlgorithm,highWaterMark 设为 highWaterMark,sizeAlgorithm 设为 sizeAlgorithm
  9. 返回 stream
写入块(write a chunk)算法,给定 FileSystemWritableFileStream streamchunk,执行如下步骤:
  1. input 设为将 chunk 转换为 FileSystemWriteChunkType 的结果。如果抛出异常,则返回带该异常的 rejected promise。
  2. p 为新的 promise。
  3. 将以下步骤加入文件系统队列:
    1. accessResultstream 的 [[file]] 的“查询访问”算法结果(模式 "readwrite")。
    2. 队列存储任务(stream 的全局对象)以执行:
      1. 如果 accessResult 权限状态不是 "granted",则 reject p,异常类型为 accessResult 的错误名,并终止。
      2. input 为字典,则 commandinput["type"],否则为 "write"。
      3. 如果 command 为 "write":
        1. 如果 input 是 undefined,或为字典但无 "data" 字段,则 reject p,异常为 TypeError,并终止。
        2. datainput["data"](若为字典)或 input 本身。
        3. writePositionstream 的 [[seekOffset]]。
        4. input 为字典且含 "position",则 writePosition 设为该值。
        5. oldSizestream 的 [[buffer]] 长度。
        6. data 是 BufferSource,则 dataBytes 为其副本。
        7. 否则若 data 是 Blob:
          1. dataBytes 设为读取 data 的结果。若抛异常则 reject p 并终止。
        8. 否则:
          1. 断言 data 是 USVString。
          2. dataBytes 设为 UTF-8 编码 data 的结果。
        9. 如果 writePosition 大于 oldSize,在 [[buffer]] 尾部追加 writePosition - oldSize 个 0x00 字节(NUL)。实现需表现为内容被 NUL 填充但可用稀疏文件。
        10. head 为 [[buffer]] 的前 writePosition 字节。
        11. tail 为空字节序列。
        12. writePosition + dataBytes 长度小于 oldSize
          1. tail 设为 [[buffer]] 的后 oldSize - (writePosition + dataBytes 长度) 字节。
        13. 将 [[buffer]] 设为 head + dataBytes + tail
        14. 若上一步操作超过存储配额,则 reject p,异常为 QuotaExceededError,并终止,且 [[buffer]] 保持原状。存储配额仅适用于 bucket 文件系统,但其他磁盘空间不足也可能导致失败。
        15. 将 [[seekOffset]] 设为 writePosition + dataBytes 长度。
        16. resolve p
      4. 否则,如果 command 为 "seek":
        1. 断言 chunk 是字典。
        2. 若无 "position",则 reject p,异常为 TypeError,并终止。
        3. 将 [[seekOffset]] 设为 chunk["position"]。
        4. resolve p
      5. 否则,如果 command 为 "truncate":
        1. 断言 chunk 是字典。
        2. 若无 "size" 字段,则 reject p,异常为 TypeError,并终止。
        3. newSizechunk["size"]。
        4. oldSize 为 [[buffer]] 长度。
        5. newSize 大于 oldSize
          1. 将 [[buffer]] 设为 [[buffer]] 加上 newSize - oldSize 个 0x00 字节。
          2. 若超出存储配额,则 reject p,异常为 QuotaExceededError,并终止,且 [[buffer]] 保持原状。存储配额仅限 bucket 文件系统,但其他磁盘空间不足也可能失败。
        6. 否则如果 newSize 小于 oldSize
          1. 将 [[buffer]] 设为前 newSize 字节。
        7. 若 [[seekOffset]] 大于 newSize,则设其为 newSize
        8. resolve p
  4. 返回 p

2.5.1. write() 方法

await stream . write(data)
await stream . write({ type: "write", data: data })

data 的内容写入与 stream 关联的文件(从当前文件游标偏移处)。

只有在关闭流后,变更才会真正写入磁盘文件。更改通常会先写入临时文件。

await stream . write({ type: "write", position: position, data: data })

data 的内容写入与 stream 关联的文件(从文件头部 position 字节处),并将文件游标更新至写入末尾位置。

只有在关闭流后,变更才会真正写入磁盘文件。更改通常会先写入临时文件。

await stream . write({ type: "seek", position: position })

将文件游标偏移更新为距文件头部 position 字节处。

await stream . write({ type: "truncate", size: size })

将与 stream 关联的文件尺寸调整为 size 字节。如果 size 大于当前文件大小,则用 null 字节补齐,否则截断文件。

当调用 truncate 时,文件游标也会被更新:若游标小于 size,保持不变;若大于 size,则设为 size。这样保证后续写入不会出错。

只有在关闭流后,变更才会真正写入磁盘文件。更改通常会先写入临时文件。

write(data) 方法的实现步骤如下:
  1. writer 为为 this 获取的 writer。
  2. result 为向 writer 写入 data 的结果。
  3. 释放 writer
  4. 返回 result

2.5.2. seek() 方法

await stream . seek(position)

将文件游标偏移更新为距文件头部 position 字节处。

seek(position) 方法的实现步骤如下:
  1. writer 为为 this 获取的 writer。
  2. result 为向 writer 写入 «[ "type" → "seek", "position" → position ]» 的结果。
  3. 释放 writer
  4. 返回 result

2.5.3. truncate() 方法

await stream . truncate(size)

将与 stream 关联的文件尺寸调整为 size 字节。如果 size 大于当前文件大小,则用 null 字节补齐,否则截断文件。

当调用 truncate 时,文件游标也会被更新:若游标小于 size,保持不变;若大于 size,则设为 size。这样保证后续写入不会出错。

只有在关闭流后,变更才会真正写入磁盘文件。更改通常会先写入临时文件。

truncate(size) 方法的实现步骤如下:
  1. writer 为为 this 获取的 writer。
  2. result 为向 writer 写入 «[ "type" → "truncate", "size" → size ]» 的结果。
  3. 释放 writer
  4. 返回 result

2.6. FileSystemSyncAccessHandle 接口

dictionary FileSystemReadWriteOptions {
  [EnforceRange] unsigned long long at;
};

[Exposed=DedicatedWorker, SecureContext]
interface FileSystemSyncAccessHandle {
  unsigned long long read(AllowSharedBufferSource buffer,
                          optional FileSystemReadWriteOptions options = {});
  unsigned long long write(AllowSharedBufferSource buffer,
                           optional FileSystemReadWriteOptions options = {});

  undefined truncate([EnforceRange] unsigned long long newSize);
  unsigned long long getSize();
  undefined flush();
  undefined close();
};

FileSystemSyncAccessHandle 具有关联的 [[file]](一个 文件条目)。

FileSystemSyncAccessHandle 具有关联的 [[state]],一个字符串,只能为 "open" 或 "closed"。

FileSystemSyncAccessHandle 是能够对单个文件进行读写、获取和更改其大小的对象。

FileSystemSyncAccessHandle 提供同步方法。这在异步操作开销较大的场景(例如 WebAssembly)中带来更高性能。

FileSystemSyncAccessHandle 有一个 文件位置游标,初始为文件头部偏移 0 字节。

创建新的 FileSystemSyncAccessHandle,给定 文件条目 fileRealm realm,步骤如下:
  1. 创建 handle,为 realm 内新的 FileSystemSyncAccessHandle
  2. handle 的 [[file]] 设为 file
  3. handle 的 [[state]] 设为 "open"。
  4. 返回 handle

2.6.1. read() 方法

handle . read(buffer)
handle . read(buffer, { at })

将与 handle 关联的文件内容读取到 buffer,可选地从指定偏移处读取。

调用 read() 时,文件游标会更新为最后读取字节的下一个字节。

应指定当外部修改文件时 Access Handle 的行为。

read(buffer, FileSystemReadWriteOptions: options) 方法的实现步骤如下:
  1. 如果 this 的 [[state]] 为 "closed",则抛出 InvalidStateError。
  2. bufferSizebuffer 的字节长度。
  3. fileContentsthis 的 [[file]] 的二进制数据。
  4. fileSizefileContents 的字节长度。
  5. readStartoptions["at"](如果存在),否则为 this 的文件位置游标。
  6. 如果底层文件系统不支持从 readStart 读取,则抛出 TypeError。
  7. 如果 readStart 大于 fileSize
    1. this 的文件位置游标设为 fileSize
    2. 返回 0。
  8. readEndreadStart + (bufferSize − 1)。
  9. 如果 readEnd 大于 fileSize,则将 readEnd 设为 fileSize
  10. bytesfileContentsreadStartreadEnd 字节序列。
  11. resultbytes 的字节长度。
  12. 如果前述步骤的读取操作失败:
    1. 如果部分读取并已知读取字节数,则设 result 为该字节数。
    2. 否则设 result 为 0。
  13. arrayBufferbuffer 的底层 buffer。
  14. bytes 写入 arrayBuffer
  15. this 的文件位置游标设为 readStart + result
  16. 返回 result

2.6.2. write() 方法

handle . write(buffer)
handle . write(buffer, { at })

buffer 的内容写入与 handle 关联的文件,可选地从指定偏移处写入,并返回写入的字节数。检查返回的字节数可用于检测和处理错误或部分写入。

调用 write 时,文件游标会更新为最后写入字节的下一个字节。

write(buffer, FileSystemReadWriteOptions: options) 方法的实现步骤如下:
  1. 如果 this 的 [[state]] 为 "closed",抛出 InvalidStateError。
  2. writePositionoptions["at"](如果存在),否则为 this 的文件位置游标。
  3. 如果底层文件系统不支持从 writePosition 写入,则抛出 TypeError。
  4. fileContentsthis 的 [[file]] 的二进制数据的副本。
  5. oldSizefileContents 的字节长度。
  6. bufferSizebuffer 的字节长度。
  7. 如果 writePosition 大于 oldSize,则在 fileContents 尾部追加 writePositionoldSize 个 0x00 字节(NUL)。实现应表现为跳过的内容被 NUL 填充,可使用稀疏文件。
  8. headfileContents 的前 writePosition 字节。
  9. tail 为空字节序列。
  10. 如果 writePosition + bufferSize 小于 oldSize
    1. tailfileContents 的后 oldSize − (writePosition + bufferSize) 字节。
  11. newSizehead 长度 + bufferSize + tail 长度。
  12. 如果 newSizeoldSize 超过可用存储配额,则抛出 QuotaExceededError。
  13. this 的 [[file]] 的二进制数据设为 head + buffer 的内容 + tail实现可直接写入操作系统,允许部分写入。
  14. 如果前述步骤修改二进制数据时失败:
    1. 如有部分写入且已知写入字节数:
      1. bytesWritten 为写入 buffer 的字节数。
      2. this 的文件位置游标设为 writePosition + bytesWritten
      3. 返回 bytesWritten
    2. 否则抛出 InvalidStateError。
  15. this 的文件位置游标设为 writePosition + bufferSize
  16. 返回 bufferSize

2.6.3. truncate() 方法

handle . truncate(newSize)

将与 handle 关联的文件长度调整为 newSize 字节。如果 newSize 大于当前文件大小,则用 null 字节填充,否则截断文件。

调用 truncate 时,如果游标小于 newSize 保持不变,否则大于 newSize 时设为 newSize

truncate(newSize) 方法的实现步骤如下:
  1. 如果 this 的 [[state]] 为 "closed",则抛出 InvalidStateError。
  2. fileContentsthis 的 [[file]] 的二进制数据的副本。
  3. oldSizethis 的 [[file]] 的二进制数据的长度。
  4. 如果底层文件系统不支持将文件大小设置为 newSize,则抛出 TypeError。
  5. 如果 newSize 大于 oldSize
    1. 如果 newSizeoldSize 超过可用存储配额,则抛出 QuotaExceededError。
    2. this 的 [[file]] 设为 fileContents 拼接 newSizeoldSize 个 0x00 字节的新字节序列。
    3. 如果上述步骤修改 [[file]] 的二进制数据失败,则抛出 InvalidStateError。
  6. 否则,如果 newSize 小于 oldSize
    1. this 的 [[file]] 设为 fileContents 的前 newSize 字节。
    2. 如果上述步骤修改 [[file]] 的二进制数据失败,则抛出 InvalidStateError。
  7. 如果 this 的文件位置游标大于 newSize,则将其设为 newSize

2.6.4. getSize() 方法

handle . getSize()

返回与 handle 关联的文件大小(字节数)。

getSize() 方法的实现步骤如下:
  1. 如果 this 的 [[state]] 为 "closed",则抛出 InvalidStateError。
  2. 返回 this 的 [[file]] 的二进制数据的字节长度。

2.6.5. flush() 方法

handle . flush()

确保与 handle 关联的文件内容包含所有通过 write() 完成的修改。

flush() 方法的实现步骤如下:
  1. 如果 this 的 [[state]] 为 "closed",则抛出 InvalidStateError。
  2. 尝试将所有已缓存的文件内容修改同步到底层存储设备。

注意: 这也叫做文件刷新。部分文件系统(如内存型文件系统)无“磁盘”可刷写,此操作可能无实际效果。

2.6.6. close() 方法

handle . close()

关闭该 access handle;若已关闭则无操作。之后无法再对其进行任何操作,并且会释放handle 关联的 [[file]] 的锁。

close() 方法的实现步骤如下:
  1. 如果 this 的 [[state]] 为 "closed",直接返回。
  2. this 的 [[state]] 设为 "closed"。
  3. lockReleased 为 false。
  4. filethis 的 [[file]]。
  5. 将以下步骤加入文件系统队列:
    1. 释放 file 的锁。
    2. lockReleased 设为 true。
  6. 暂停,直到 lockReleased 为 true。

注意: 该方法不保证所有文件修改会立即写入底层存储。如需此保证,应先调用 flush()

3. 访问 Bucket 文件系统

bucket 文件系统 是一种 存储端点,其 标识符"fileSystem"类型« "local" »配额为 null。

存储端点应在 [storage] 标准中定义,而不是在此处定义。应将其合并到那里的表格中。

注意: 虽然用户代理通常会通过将 bucket 文件系统 的内容持久化到磁盘来实现,但并不意味着用户可以轻易访问其内容。同样,也不要求磁盘上实际存在与 bucket 文件系统 子项同名的文件或目录。

[SecureContext]
partial interface StorageManager {
  Promise<FileSystemDirectoryHandle> getDirectory();
};
directoryHandle = await navigator . storage . getDirectory()

返回 bucket 文件系统 的根目录。

getDirectory() 方法的实现步骤如下:
  1. environment当前设置对象

  2. map 为以 environment"fileSystem" 运行 获取本地存储 bottle 映射 的结果。如果返回失败,则返回带拒绝的 promise,错误为 "SecurityError" 的 DOMException

  3. 如果 map["root"] 不存在

    1. dir 为新建的 目录条目,其 查询访问请求访问算法始终返回文件系统访问结果,其权限状态为 "granted",error name 为空字符串。

    2. dirname 设为空字符串。

    3. dirchildren 设为空有序集合

    4. map["root"] 设为 dir

  4. root实现自定义的不透明字符串

  5. path 为 « 空字符串 »。

  6. handle创建新的 FileSystemDirectoryHandle,参数为 rootpath,作用域为当前 Realm 的结果。

    注意: root 可能包含如存储桶等相关标识信息。

  7. 断言:以 handlelocator 执行定位条目,返回的目录条目map["root"]相同

  8. 返回带解析的 promise,值为 handle

致谢

特别感谢 Alex Danilo, Anne van Kesteren, Anoesj Sadraee, Austin Sullivan, Chase Phillips, Daseul Lee, Dru Knox, Edgar Chen, Emanuel Krivoy, Hazim Mohamed, Ingvar Stepanyan, Jari Jalkanen, Joshua Bell, Kagami Sascha Rosylight, Marcos Cáceres, Martin Thomson, Olivier Yiptong, Philip Jägenstedt, Randell Jesup, Richard Stotz, Ruth John, Sid Vishnoi, Sihui Liu, Stefan Sauer, Thomas Steiner, Victor Costan,以及 Youenn Fablet 的大力支持!

本标准由 Marijn KruisselbrinkGooglemek@chromium.org)编写。

知识产权声明

本 Living Standard 包含来自 W3C WICG 的 File System Access 的内容,依据 W3C 软件和文档许可 提供。

版权所有 © WHATWG(Apple,Google,Mozilla,Microsoft)。本作品采用 知识共享署名 4.0 国际许可协议 许可。若部分内容被纳入源代码,则该部分源代码采用 BSD 3-Clause 许可证

本规范为 Living Standard。如需专利审查版本,请参见 Living Standard Review Draft

索引

本规范定义的术语

引用中定义的术语

参考文献

规范性引用

[ECMASCRIPT]
ECMAScript 语言规范。URL: https://tc39.es/ecma262/multipage/
[ENCODING]
Anne van Kesteren. 编码标准(Encoding Standard)。Living Standard。URL: https://encoding.spec.whatwg.org/
[FILE-API]
Marijn Kruisselbrink. File API。URL: https://w3c.github.io/FileAPI/
[HTML]
Anne van Kesteren 等。HTML 标准。Living Standard。URL: https://html.spec.whatwg.org/multipage/
[INFRA]
Anne van Kesteren;Domenic Denicola。Infra 标准。Living Standard。URL: https://infra.spec.whatwg.org/
[PERMISSIONS]
Marcos Caceres;Mike Taylor。Permissions。URL: https://w3c.github.io/permissions/
[PERMISSIONS-REQUEST]
请求权限(Requesting Permissions)。cg-draft。URL: https://wicg.github.io/permissions-request/
[STORAGE]
Anne van Kesteren。存储标准(Storage Standard)。Living Standard。URL: https://storage.spec.whatwg.org/
[STREAMS]
Adam Rice 等。Streams 标准。Living Standard。URL: https://streams.spec.whatwg.org/
[WEBIDL]
Edgar Chen;Timothy Gu。Web IDL 标准。Living Standard。URL: https://webidl.spec.whatwg.org/

IDL 索引

enum FileSystemHandleKind {
  "file",
  "directory",
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemHandle {
  readonly attribute FileSystemHandleKind kind;
  readonly attribute USVString name;

  Promise<boolean> isSameEntry(FileSystemHandle other);
};

dictionary FileSystemCreateWritableOptions {
  boolean keepExistingData = false;
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemFileHandle : FileSystemHandle {
  Promise<File> getFile();
  Promise<FileSystemWritableFileStream> createWritable(optional FileSystemCreateWritableOptions options = {});
  [Exposed=DedicatedWorker]
  Promise<FileSystemSyncAccessHandle> createSyncAccessHandle();
};

dictionary FileSystemGetFileOptions {
  boolean create = false;
};

dictionary FileSystemGetDirectoryOptions {
  boolean create = false;
};

dictionary FileSystemRemoveOptions {
  boolean recursive = false;
};

[Exposed=(Window,Worker), SecureContext, Serializable]
interface FileSystemDirectoryHandle : FileSystemHandle {
  async_iterable<USVString, FileSystemHandle>;

  Promise<FileSystemFileHandle> getFileHandle(USVString name, optional FileSystemGetFileOptions options = {});
  Promise<FileSystemDirectoryHandle> getDirectoryHandle(USVString name, optional FileSystemGetDirectoryOptions options = {});

  Promise<undefined> removeEntry(USVString name, optional FileSystemRemoveOptions options = {});

  Promise<sequence<USVString>?> resolve(FileSystemHandle possibleDescendant);
};

enum WriteCommandType {
  "write",
  "seek",
  "truncate",
};

dictionary WriteParams {
  required WriteCommandType type;
  unsigned long long? size;
  unsigned long long? position;
  (BufferSource or Blob or USVString)? data;
};

typedef (BufferSource or Blob or USVString or WriteParams) FileSystemWriteChunkType;

[Exposed=(Window,Worker), SecureContext]
interface FileSystemWritableFileStream : WritableStream {
  Promise<undefined> write(FileSystemWriteChunkType data);
  Promise<undefined> seek(unsigned long long position);
  Promise<undefined> truncate(unsigned long long size);
};

dictionary FileSystemReadWriteOptions {
  [EnforceRange] unsigned long long at;
};

[Exposed=DedicatedWorker, SecureContext]
interface FileSystemSyncAccessHandle {
  unsigned long long read(AllowSharedBufferSource buffer,
                          optional FileSystemReadWriteOptions options = {});
  unsigned long long write(AllowSharedBufferSource buffer,
                           optional FileSystemReadWriteOptions options = {});

  undefined truncate([EnforceRange] unsigned long long newSize);
  unsigned long long getSize();
  undefined flush();
  undefined close();
};


[SecureContext]
partial interface StorageManager {
  Promise<FileSystemDirectoryHandle> getDirectory();
};