Web 平台越来越多地为开发者提供他们所需的工具,以便他们构建经过微调的高性能 Web 应用。最值得注意的是,WebAssembly (Wasm) 为构建快速而强大的 Web 应用打开了大门,而 Emscripten 等技术现在允许开发者在网络上重复使用经过试用和测试的代码。为了真正发挥这一潜力,开发者在存储方面必须具备同样的能力和灵活性。
这正是 Storage Foundation API 的用武之地。Storage Foundation API 是一种创新的快速存储 API,它为网络带来了众多需求量很大的新用例,例如实现高性能数据库和妥善管理大型临时文件。借助这个新接口,开发者可以在 Web 中“自带存储空间”,减少 Web 代码和平台专用代码之间的功能差距。
Storage Foundation API 旨在类似于一个非常基本的文件系统,因此它通过提供通用、简单且高性能的基元为开发者提供灵活性,让他们可以在这些基元上构建更高级别的组件。应用可以利用符合其需求的最佳工具,在易用性、性能和可靠性之间找到适当的平衡。
Web 为什么需要其他 Storage API?
Web 平台为开发者提供了许多存储方案,每个方案的构建考虑了特定的使用场景。
- 其中一些方案显然与此方案不重叠,因为它们仅允许存储非常少量的数据,例如 Cookie 或由
sessionStorage
和localStorage
机制组成的 Web Storage API。 - 其他选项已因各种原因而已弃用,例如 File and Directory Entries API 或 WebSQL。
- File System Access API 具有类似的 API Surface,但其用途是与客户端的文件系统进行交互,并提供对可能不在来源(甚至是浏览器)所有权范围之内的数据的访问权限。这种不同的关注点具有更严格的安全注意事项和更高的性能成本。
- IndexedDB API 可用作 Storage Foundation API 某些用例的后端。例如,Emscripten 包含 IDBFS,这是一个基于 IndexedDB 的永久性文件系统。但是,由于 IndexedDB 从根本上来讲是键值对存储区,因此存在显著的性能限制。此外,在 IndexedDB 下,直接访问文件的子部分会变得更加困难和缓慢。
- 最后,CacheStorage 接口获得广泛支持并经过调整,用于存储大型数据(例如 Web 应用资源),但值是不可变的。
Storage Foundation API 旨在通过支持高效存储在应用来源中定义的可变大型文件,来弥补之前存储方案的所有不足之处。
建议的 Storage Foundation API 用例
可以使用此 API 的网站示例包括:
- 处理大量视频、音频或图片数据的效率或创意应用。此类应用可以将细分分流到磁盘,而不是将其保存在内存中。
- 依赖于可从 Wasm 访问的永久性文件系统并且需要比 IDBFS 所能保证的性能更高的应用。
什么是 Storage Foundation API?
该 API 包含两个主要部分:
- 文件系统调用 - 提供与文件和文件路径交互的基本功能。
- 文件句柄,可提供对现有文件的读写权限。
文件系统调用
Storage Foundation API 引入了一个新对象 storageFoundation
,该对象基于 window
对象,其中包含许多函数:
storageFoundation.open(name)
:打开具有给定名称的文件(如果存在),否则创建一个新文件。返回一个使用打开的文件进行解析的 promise。
storageFoundation.delete(name)
:移除具有给定名称的文件。返回一个在删除文件时解析的 promise。storageFoundation.rename(oldName, newName)
:以原子方式将文件从旧名称重命名为新名称。返回一个在重命名文件时进行解析的 promise。storageFoundation.getAll()
:返回一个使用包含所有现有文件名数组进行解析的 promise。storageFoundation.requestCapacity(requestedCapacity)
:请求新容量(以字节为单位),以供当前执行上下文使用。返回一个使用剩余可用容量进行解析的 promise。
storageFoundation.releaseCapacity(toBeReleasedCapacity)
:从当前执行上下文中释放指定数量的字节数,并返回使用剩余容量进行解析的 promise。storageFoundation.getRemainingCapacity()
:返回一个使用当前执行上下文可用的容量进行解析的 promise。
文件句柄
处理文件的方式如下:
NativeIOFile.close()
:关闭文件,并返回在操作完成时解析的 promise。NativeIOFile.flush()
:与存储设备同步(即刷新)文件的内存中状态,并返回在操作完成时解析的 promise。
NativeIOFile.getLength()
:返回一个使用文件长度(以字节为单位)进行解析的 promise。NativeIOFile.setLength(length)
:设置文件的长度(以字节为单位),并返回在操作完成时解析的 promise。如果新长度小于当前长度,则从文件末尾开始移除字节。否则,系统会使用零值字节扩展文件。NativeIOFile.read(buffer, offset)
:通过一个缓冲区(该缓冲区是传输指定缓冲区的结果,然后保持分离状态)以给定偏移量读取文件的内容。返回一个NativeIOReadResult
,其中包含传输的缓冲区和成功读取的字节数。NativeIOReadResult
是由以下两个条目组成的对象:buffer
:ArrayBufferView
,是传输传递到read()
的缓冲区的结果。其类型和长度与源缓冲区相同。readBytes
:成功读取到buffer
的字节数。如果发生错误或读取范围超出文件末尾,该范围可能会小于缓冲区空间。 如果读取范围超出文件末尾,则设为零。
NativeIOFile.write(buffer, offset)
:按给定偏移量将给定缓冲区的内容写入文件。该缓冲区在写入任何数据之前进行传输,因此该缓冲区保持分离状态。返回一个NativeIOWriteResult
,其中包含传输的缓冲区和成功写入的字节数。如果写入范围超过其长度,文件将被扩展。NativeIOWriteResult
是由以下两个条目组成的对象:buffer
:一个ArrayBufferView
,是传输传递到write()
的缓冲区的结果。它的类型和长度与源缓冲区相同。writtenBytes
:成功写入buffer
的字节数。如果出现错误,该值可能会小于缓冲区空间。
完整示例
为了更清楚地理解上面介绍的概念,下面提供了两个完整的示例,可帮助您了解 Storage Foundation 文件生命周期的不同阶段。
打开,写入,阅读,关闭
// Open a file (creating it if needed).
const file = await storageFoundation.open('test_file');
try {
// Request 100 bytes of capacity for this context.
await storageFoundation.requestCapacity(100);
const writeBuffer = new Uint8Array([64, 65, 66]);
// Write the buffer at offset 0. After this operation, `result.buffer`
// contains the transferred buffer and `result.writtenBytes` is 3,
// the number of bytes written. `writeBuffer` is left detached.
let result = await file.write(writeBuffer, 0);
const readBuffer = new Uint8Array(3);
// Read at offset 1. `result.buffer` contains the transferred buffer,
// `result.readBytes` is 2, the number of bytes read. `readBuffer` is left
// detached.
result = await file.read(readBuffer, 1);
// `Uint8Array(3) [65, 66, 0]`
console.log(result.buffer);
} finally {
file.close();
}
打开、列出、删除
// Open three different files (creating them if needed).
await storageFoundation.open('sunrise');
await storageFoundation.open('noon');
await storageFoundation.open('sunset');
// List all existing files.
// `["sunset", "sunrise", "noon"]`
await storageFoundation.getAll();
// Delete one of the three files.
await storageFoundation.delete('noon');
// List all remaining existing files.
// `["sunrise", "noon"]`
await storageFoundation.getAll();
演示
您可以观看下方嵌入的 Storage Foundation API 演示。您可以创建、重命名、写入和读取文件,以及在进行更改时查看已请求更新的可用容量。您可以在 Glitch 上找到该演示的源代码。
安全与权限
Chromium 团队根据控制对强大的 Web 平台功能的访问权限中定义的核心原则(包括用户控制、透明度和人体工程学)设计和实现 Storage Foundation API。
对 Storage Foundation API 的访问遵循与网络上其他现代存储 API 相同的模式,受源站限制,这意味着源源只能访问自己创建的数据。这也仅限于安全上下文。
用户控制
存储空间配额将用于分配对磁盘空间的访问权限,并防止滥用行为。需要先请求要占用的内存。与其他存储 API 一样,用户可以通过浏览器清除 Storage Foundation API 占用的空间。
实用链接
- 公开铺垫消息
- Storage Foundation API 演示 | Storage Foundation API 演示源代码
- Chromium 跟踪错误
- ChromeStatus.com 条目
- Blink 组件:
Blink>Storage>NativeIO
- 代码审核
- 有意进行原型设计
- WebKit 线程
- Mozilla 线程
致谢
Storage Foundation API 由 Emanuel Krivoy 和 Richard Stotz 指定和实现。本文由 Pete LePage 和 Joe Medley 审核。
主打图片(通过 Markus Spiske 在 Un 一旦您启动了)。