文件系统(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 convertingFileSystemWriteChunkType 的结果。 如果此步骤抛出异常,则返回 a promise rejected with 该异常。

  2. pa new promise

  3. Enqueue the following stepsfile system queue

    1. accessResult 为运行 stream[[file]]query access,给定 "readwrite" 的结果。

    2. Queue a storage task,以 streamrelevant global object 运行以下步骤:

      1. 如果 accessResultpermission state 不是 "granted", 则以一个 DOMException (其名称为 accessResulterror namereject p,并中止这些步骤。

      2. commandinput["type"] (若 input 是一个 dictionary);否则为 "write"。

      3. 如果 command 是 "write":

        1. 如果 inputundefined,或 input 是一个 dictionaryinput["data"] 不 exist, 则以一个 TypeError reject p 并中止这些步骤。

        2. datainput["data"] (若 input 是一个 dictionary);否则为 input

        3. writePositionstream[[seekOffset]]

        4. 如果 input 是一个 dictionaryinput["position"] exists,则将 writePosition 设为 input["position"]。

        5. oldSizestream[[buffer]]length

        6. 如果 data 是一个 BufferSource, 令 dataBytesa copy of data

        7. 否则,如果 data 是一个 Blob

          1. dataBytes 为在 data 上执行 read operation 的结果。若抛出异常,reject p 并中止这些步骤。

        8. 否则:

          1. Assertdata 是一个 USVString

          2. dataBytes 为对 data 进行 UTF-8 encoding 的结果。

        9. 如果 writePosition 大于 oldSize, 则在 stream[[buffer]] 末尾 追加 writePosition - oldSize 个 0x00 (NUL) 字节。

          Note: 期望实现表现为被跳过的文件内容确实用 NUL 字节填充。这并不意味着这些字节必须真的写入磁盘并占用空间。多数文件系统支持所谓稀疏文件,即这些 NUL 字节不占用实际磁盘空间。

        10. head 为一个包含 stream[[buffer]]writePosition 个字节的 byte sequence

        11. tail 为空的 byte sequence

        12. 如果 writePosition + datalength 小于 oldSize

          1. tail 为一个 byte sequence,包含 stream[[buffer]] 最后 oldSize - (writePosition + datalength) 个字节。

        13. stream[[buffer]] 设为 headdatatail 的连接。

        14. 如果前面修改 stream[[buffer]] 的操作因超出 storage quota 而失败, 则以一个 QuotaExceededError reject p 并中止这些步骤, 且保持 stream[[buffer]] 不变。

          Note: Storage quota 仅适用于存储在 bucket file system 中的文件。但此操作仍可能在其他文件上失败,例如目标磁盘空间不足。

        15. stream[[seekOffset]] 设为 writePosition + datalength

        16. Resolve p

      4. 否则,如果 command 是 "seek":

        1. Assertchunk 是一个 dictionary

        2. 如果 chunk["position"] 不 exist, 则以一个 TypeError reject p 并中止这些步骤。

        3. stream[[seekOffset]] 设为 chunk["position"]。

        4. Resolve p

      5. 否则,如果 command 是 "truncate":

        1. Assertchunk 是一个 dictionary

        2. 如果 chunk["size"] 不 exist, 则以一个 TypeError reject p 并中止这些步骤。

        3. newSizechunk["size"]。

        4. oldSizestream[[buffer]]length

        5. 如果 newSize 大于 oldSize

          1. stream[[buffer]] 设为一个 byte sequence, 由 stream[[buffer]] 与包含 newSize-oldSize0x00 字节的 byte sequence 拼接而成。

          2. 如果前一步操作因超出 storage quota 而失败, 则以一个 QuotaExceededError reject p 并中止这些步骤, 且保持 stream[[buffer]] 不变。

            Note: Storage quota 仅适用于存储在 bucket file system 中的文件。但此操作仍可能在其他文件上失败,例如目标磁盘空间不足。

        6. 否则,如果 newSize 小于 oldSize

          1. stream[[buffer]] 设为一个 byte sequence,包含 stream[[buffer]] 中前 newSize 个字节。

        7. 如果 stream[[seekOffset]] 大于 newSize, 则将 stream[[seekOffset]] 设为 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 Handles 应当如何响应。 [Issue #35]

read(buffer, FileSystemReadWriteOptions: options) 方法的步骤如下:
  1. 如果 this[[state]] 为 "closed", throw 一个 "InvalidStateError" DOMException

  2. bufferSizebufferbyte length

  3. fileContentsthis[[file]]binary data

  4. fileSizefileContentslength

  5. readStartoptions["at"] (如果 options["at"] exists);否则为 thisfile position cursor

  6. 如果底层文件系统不支持从偏移 readStart 读取, throw 一个 TypeError

  7. 如果 readStart 大于 fileSize

    1. thisfile position cursor 设为 fileSize

    2. 返回 0。

  8. readEndreadStart + (bufferSize − 1)。

  9. 如果 readEnd 大于 fileSize,则将 readEnd 设为 fileSize

  10. bytes 为一个 byte sequence,包含 fileContents 中从 readStartreadEnd 的字节。

  11. resultbyteslength

  12. 如果前面从 fileContents 读取的操作失败:

    1. 如果发生部分读取且读入 bytes 的字节数已知, 将 result 设为已读取的字节数。

    2. 否则将 result 设为 0。

  13. arrayBufferbufferunderlying buffer

  14. Write bytesarrayBuffer 中。

  15. thisfile position cursor 设为 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", throw 一个 "InvalidStateError" DOMException

  2. writePositionoptions["at"] (如果 options["at"] exists);否则为 thisfile position cursor

  3. 如果底层文件系统不支持写入到偏移 writePositionthrow 一个 TypeError

  4. fileContentsthis[[file]]binary data 的一个副本。

  5. oldSizefileContentslength

  6. bufferSizebufferbyte length

  7. 如果 writePosition 大于 oldSize, 则在 fileContents 末尾追加 writePositionoldSize 个 0x00 (NUL) 字节。

    Note: 期望实现表现为被跳过的文件内容确实用 NUL 字节填充。这并不意味着这些字节必须真的写入磁盘并占用空间。多数文件系统支持所谓稀疏文件(sparse files),这些 NUL 字节不占用实际磁盘空间。

  8. head 为一个 byte sequence,包含 fileContents 的前 writePosition 个字节。

  9. tail 为空的 byte sequence

  10. 如果 writePosition + bufferSize 小于 oldSize

    1. tail 设为一个 byte sequence,包含 fileContents 最后 oldSize − (writePosition + bufferSize) 个字节。

  11. newSizeheadlength + bufferSize + taillength

  12. 如果 newSizeoldSize 超出可用 storage quotathrow 一个 QuotaExceededError

  13. this[[file]]binary data 设为 headbuffer 的内容与 tail 的连接。

    Note: 用于访问 buffer 内容的机制被刻意保持模糊。实现很可能为了性能而直接对宿主操作系统发出写调用(而不是创建 buffer 的副本),这使得无法详细规定写入顺序及部分写入的结果。

  14. 如果前面修改 this[[file]]binary data 的操作失败:

    1. 如果发生部分写入且从 buffer 写入的字节数已知:

      1. bytesWritten 为从 buffer 写入的字节数。

      2. thisfile position cursor 设为 writePosition + bytesWritten

      3. 返回 bytesWritten

    2. 否则 throw 一个 "InvalidStateError" DOMException

  15. thisfile position cursor 设为 writePosition + bufferSize

  16. 返回 bufferSize

2.6.3. truncate() 方法

handle . truncate(newSize)

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

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

The truncate(newSize) 方法的步骤为:
  1. 如果 this[[state]] 为 "closed", 抛出 一个 "InvalidStateError" DOMException

  2. fileContentsthis[[file]]二进制数据 的一份副本。

  3. oldSize长度(即 this[[file]]二进制数据 的长度)。

  4. 如果底层文件系统不支持将文件大小设为 newSize,则 抛出 一个 TypeError

  5. 如果 newSize 大于 oldSize

    1. 如果 newSizeoldSize 超过了可用的 存储配额抛出 一个 QuotaExceededError

    2. this[[file]] 设为一个 字节序列,该字节序列由 fileContents 与一个 包含 newSizeoldSize 个 0x00 字节的 字节序列 连接而成。

    3. 如果前述步骤中修改 this[[file]]二进制数据 的操作失败, 抛出 一个 "InvalidStateError" DOMException

  6. 否则,如果 newSize 小于 oldSize

    1. this[[file]] 设为一个 字节序列,该序列包含 fileContents 中 前 newSize 个字节。

    2. 如果前述步骤中修改 this[[file]]二进制数据 的操作失败, 抛出 一个 "InvalidStateError" DOMException

  7. 如果 this文件位置游标 大于 newSize,则将 文件位置游标 设为 newSize

2.6.4. getSize() 方法

handle . getSize()

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

getSize() 方法的步骤为:
  1. 如果 this[[state]] 为 "closed", 抛出 一个 "InvalidStateError" DOMException

  2. 返回 this[[file]]二进制数据长度

2.6.5. flush() 方法

handle . flush()

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

flush() 方法的步骤为:
  1. 如果 this[[state]] 为 "closed", 抛出 一个 "InvalidStateError" DOMException

  2. 尝试将该文件内容的所有已缓存修改传输到底层文件系统的存储设备。

    注意: 这也被称为刷新 (flushing)。在某些文件系统(例如内存文件系统)上可能是空操作,因为它们没有可刷新到的“磁盘”。

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();
};