1. 介绍
多年来,网页已经发展出各种可以用于存储的API,例如
IndexedDB、localStorage
和showNotification()
。存储标准通过定义以下内容来整合这些API:
- 一个存储桶,这些API存储其数据的基本单位
- 使该存储桶持久化的方法
- 一种获取源的使用量和配额估算的方法
传统上,当用户设备上的存储空间不足时,这些API存储的数据会丢失,用户无法干预。然而,持久化存储桶不能在未经用户同意的情况下被清除。这为网页带来了用户在本地平台上享受的数据保障。
使存储持久化的一种简单方法是调用persist()
方法。它同时向终端用户请求权限,并在获得权限后将存储更改为持久化:
navigator. storage. persist(). then( persisted => {
if ( persisted) {
/* … */
}
});
为了避免在没有预先通知的情况下向终端用户显示由用户代理驱动的对话框,可以编写稍微复杂一些的代码:
Promise. all([
navigator. storage. persisted(),
navigator. permissions. query({ name: "persistent-storage" })
]). then(([ persisted, permission]) => {
if ( ! persisted && permission. state == "granted" ) {
navigator. storage. persist(). then( /* … */ );
} else if ( ! persisted && permission. state == "prompt" ) {
showPersistentStorageExplanation();
}
});
estimate()
方法可以用来确定是否有足够的空间来存储应用程序的内容:
function retrieveNextChunk( nextChunkInfo) {
return navigator. storage. estimate(). then( info => {
if ( info. quota - info. usage > nextChunkInfo. size) {
return fetch( nextChunkInfo. url);
} else {
throw new Error ( "insufficient space to store next chunk" );
}
}). then( /* … */ );
}
2. 术语
本规范依赖于Infra标准。[INFRA]
本规范使用HTML、IDL和权限标准中的术语。[HTML] [WEBIDL] [PERMISSIONS]
3. 基础设施
用户代理具有各种类型的半持久性状态:
- 凭据
-
最终用户凭据,如通过HTML表单提交的用户名和密码
- 权限
-
各种功能的权限,如地理位置
- 网络
-
HTTP缓存、Cookies、身份验证条目、TLS客户端证书
- 存储
- Indexed DB、Cache API、服务工作者注册、
localStorage
、sessionStorage
、应用缓存、通知等。
本标准主要关注存储。
4. 模型
定义本地或会话存储API的标准将定义存储端点并通过更改本标准来注册它们。这些标准将调用获取本地存储瓶映射或获取会话存储瓶映射算法,这将为它们提供:
-
失败,这可能意味着API必须抛出异常或以其他方式表明该环境设置对象没有可用的存储。
-
一个存储代理映射,其操作类似于映射,可用于以适合API的方式存储数据。本标准负责将这些数据与其他API、存储密钥和存储类型隔离开来。
如果你正在为此类API定义标准,建议针对本标准提交一个issue以寻求帮助和审查。
为了隔离这些数据,本标准定义了存储棚,它通过存储密钥将存储架进行分段。存储架又由一个存储桶组成,未来可能会由多个存储桶组成,以允许不同的存储策略。最后,存储桶由存储瓶组成,每个存储端点一个。
4.1. 存储端点
存储端点是一个本地或会话存储API,它使用本标准定义的基础架构,尤其是存储瓶,来跟踪其存储需求。
存储端点还具有一个配额,它是null或代表每个与此存储端点对应的配额(以字节为单位)的推荐数值。
存储标识符是一个ASCII字符串。
存储类型为“local
”或“session
”。
标识符 | 类型 | 配额 |
---|---|---|
"caches "
| « "local " »
| null |
"indexedDB "
| « "local " »
| null |
"localStorage "
| « "local " »
| 5 × 220 (即5 Mebibytes) |
"serviceWorkerRegistrations "
| « "local " »
| null |
"sessionStorage "
| « "session " »
| 5 × 220 (即5 Mebibytes) |
如前所述,标准可以将这些存储标识符与获取本地存储瓶映射和获取会话存储瓶映射一起使用。预计未来某些API将适用于这两种存储类型。
4.2. 存储键
这预计会有所改变;请参见客户端存储分区。
要获取一个存储键,给定一个环境environment,执行以下步骤:
-
让key成为运行获取用于非存储目的的存储键与environment后的结果。
-
如果用户禁用了存储,则返回失败。
-
返回key。
要获取用于非存储目的的存储键,给定一个环境environment,执行以下步骤:
4.3. 存储棚
用户代理持有一个存储棚,这是一个存储棚。用户代理的存储棚持有所有的本地存储数据。
可遍历的导航对象持有一个存储棚,这是一个存储棚。可遍历的导航对象的存储棚持有所有的会话存储数据。
要遗留克隆一个可遍历存储棚,给定一个可遍历的导航对象A和一个可遍历的导航对象B,执行以下步骤:
这被认为是遗留的,因为其益处(如果有的话)并不超过实现的复杂性。因此,它不会被扩展或在HTML之外使用。[HTML]
4.4. 存储架
存储架为存储棚中的每个存储键存在。它持有一个桶映射,这是一个将字符串映射到存储桶的映射。
目前,"default
"是桶映射中唯一存在的键。参见issue #2。当第一次获取一个存储架时,将为其赋予值。
要获取一个存储架,给定一个存储棚shed、一个环境设置对象environment和一个存储类型type,运行以下步骤:
-
将key设为通过environment运行获取存储键的结果。
-
如果key失败,则返回失败。
-
返回shed[key]。
要获取一个本地存储架,给定一个环境设置对象environment,返回通过用户代理的存储棚、environment和"local
"运行获取存储架的结果。
要创建一个存储架,给定一个存储类型type,运行以下步骤:
4.5. 存储桶
存储桶是存储端点存储数据的地方。
本地存储桶有一个模式,其值为"best-effort
"或"persistent
"。最初值为"best-effort
"。
要创建一个存储桶,给定一个存储类型type,运行以下步骤:
-
将bucket设为null。
-
如果type为"
local
",则将bucket设为一个新的本地存储桶。 -
否则:
-
断言:type为"
session
"。 -
将bucket设为一个新的会话存储桶。
-
-
对于 endpoint中的每一个注册的存储端点,若其类型 包含 type,则将bucket的瓶映射[endpoint的标识符]设为一个新的存储瓶,其配额为endpoint的配额。
-
返回bucket。
4.6. 存储瓶
存储瓶是存储桶的一部分,为单个存储端点划分出来。存储瓶有一个映射,最初为空映射。存储瓶还有一个代理映射引用集,最初为空集合。存储瓶还有一个配额,其值为null或表示它可以容纳的总字节数的保守估计。null表示没有限制。它仍然受其所包含的存储架的存储配额的约束。
存储瓶的映射是存储实际数据的位置。用户代理应存储这些数据,并以代理甚至代理集群边界的方式在实现定义的方式中跨越这些边界存储和访问这些内容,使本标准及使用本标准的标准能够访问这些内容。
要获取存储瓶映射,给定一个存储类型type、环境设置对象environment和存储标识符identifier,运行以下步骤:
-
将shed设为null。
-
如果type为"
local
",则将shed设为用户代理的存储棚。 -
否则:
-
断言:type为"
session
"。 -
将shed设为environment的全局对象的关联
Document
的节点可导航的可遍历导航的存储棚。
-
-
让shelf成为运行获取存储架的结果,传递shed、environment和type。
-
如果shelf失败,则返回失败。
-
让bucket成为shelf的桶映射["
default
"]。 -
让bottle成为bucket的瓶映射[identifier]。
-
返回proxyMap。
要获取本地存储瓶映射,给定环境设置对象environment和存储标识符identifier,返回运行获取存储瓶映射的结果,传递"local
"、environment和identifier。
要获取会话存储瓶映射,给定环境设置对象environment和存储标识符identifier,返回运行获取存储瓶映射的结果,传递"session
"、environment和identifier。
4.7. 存储代理映射
存储代理映射等同于映射,只是所有操作都改为在其支持映射上执行。
这允许替换支持映射。这对于问题#4和可能的存储访问API是必要的。
4.8. 存储任务源
存储任务源是用于与存储端点相关的所有任务的任务源。特别是那些与存储端点的配额相关的任务。
5. 持久化权限
只有当用户(或用户代理代表用户)授予使用"persistent-storage
"强大功能的权限时,本地存储桶才能将其模式更改为"persistent
"。
当授予给源时,持久化权限可用于保护存储免受用户代理的清理策略影响。用户代理在没有源或用户参与的情况下不能清理标记为持久化的存储。这对于用户需要离线使用的资源或用户在本地创建的资源特别有用。
"persistent-storage
"强大功能的权限相关算法和类型默认是一样的,除了以下几点:
- 权限状态
- 权限撤销算法
6. 使用量和配额
这不能是一个精确的数字,因为用户代理可能会(并且鼓励这样做)使用去重、压缩和其他技术,这些技术会模糊存储架实际使用的字节数。
存储配额是存储架能容纳的总字节数的实现定义的保守估计。这一数值应小于设备上的总存储空间。它不能是设备上可用存储空间的函数。
7. 管理
每当用户代理清除存储桶时,必须将其完全清除。用户代理应避免在能够访问它们的脚本正在运行时清除存储桶,除非用户另有指示。
如果删除存储桶导致其包含的存储架的桶映射为空空,则从其包含的存储棚中移除该存储架及相应的存储键。
7.1. 存储压力
当用户代理面临存储压力时,应清除网络状态和模式为“尽力而为
”的本地存储桶,理想情况下,优先以最不影响用户的方式进行清除。
如果用户代理持续面临存储压力,则用户代理应通知用户并提供一种清除剩余本地存储桶(即模式为“持久
”的那些)的方式。
如果用户代理允许恢复可遍历导航,例如通过重新打开可遍历导航或在重新启动用户代理后继续使用它们,那么清除过程必然涉及更复杂的一组启发式方法。
7.2. 用户界面指南
用户代理应为用户提供清除单个网站的网络状态和存储的能力。用户代理在其用户界面中不应区分网络状态和存储。这确保了网络状态不能用于恢复存储,并减少了用户需要注意的概念数量。
凭据应分开处理,因为它们包含用户可能无法恢复的数据,例如自动生成的密码。权限也最好分开处理,以避免给用户带来不便。
8. API
[SecureContext ]interface mixin { [
NavigatorStorage SameObject ]readonly attribute StorageManager storage ; };Navigator includes NavigatorStorage ;WorkerNavigator includes NavigatorStorage ;
每个环境设置对象都有一个关联的StorageManager
对象。[HTML]
storage
的getter步骤是返回this的相关设置对象的StorageManager
对象。
[SecureContext ,Exposed =(Window ,Worker )]interface {
StorageManager Promise <boolean >persisted (); [Exposed =Window ]Promise <boolean >persist ();Promise <StorageEstimate >estimate (); };dictionary {
StorageEstimate unsigned long long ;
usage unsigned long long ; };
quota
persisted()
方法的步骤为:
persist()
方法的步骤为:
-
令promise为一个新的 promise。
-
否则,并行执行以下步骤:
-
返回promise。
estimate()
方法的步骤为:
-
令promise为一个新的 promise。
-
否则,并行执行以下步骤:
-
返回promise。
致谢
由此,特别感谢Adrian Bateman,Aislinn Grigas,Alex Russell,Ali Alabbas,Andrew Sutherland,Andrew Williams,Austin Sullivan,Ben Kelly,Ben Turner,Dale Harvey,David Grogan,Domenic Denicola,fantasai,Jake Archibald,Jeffrey Yasskin,Jesse Mykolyn,Jinho Bang,Jonas Sicking,Joshua Bell,Kenji Baheux,Kinuko Yasuda,Luke Wagner,Michael Nordman,Mike Taylor,Mounir Lamouri,Shachar Zohar,黃強 (Shawn Huang),簡冠庭 (Timothy Guan-tin Chien),以及Victor Costan,感谢你们的出色贡献!
本标准由Anne van Kesteren(Apple,annevk@annevk.nl)撰写。
知识产权
版权 © WHATWG(Apple,Google,Mozilla,Microsoft)。本作品采用知识共享署名 4.0 国际许可协议进行许可。对于纳入源代码的部分,此类部分在源代码中根据BSD 3-Clause 许可证进行许可。
这是现行标准。有兴趣获取专利审查版本的人士应查看现行标准审查草案。