浏览器 IndexedDB 存储限制

- hikerpig
#IndexedDB#WebStorage#Browser

以下列出在 夺目 产品的开发中,对 WebStorage 和 IndexedDB 使用的一些经验。

WebStorage 存储空间限制

首先,存储分为两种:

  1. 临时存储 (Temporary Storage),默认类型,之后可能会被浏览器删除。
  2. 持久存储 (Persistent Storage),不会被浏览器自动删除,可以使用 StorageManager::persist() 方法将此域名的存储类型转为持久型,此时浏览器会弹出弹窗征求用户许可,用户也可以选择拒绝。

根据 MDN 的文档,浏览器分配给数据存储的磁盘空间大小是动态的,会根据磁盘剩余空间决定,同时对于一个具体域名下的空间分配也有一定策略。

Firefox 的策略为:

  1. 全局限制 (Global Limit),50% 剩余磁盘空间
  2. 域名组限制,大约是 20% 的全局限制量。例如 mozilla.org/joe.blogs.mozilla.org/firefox.com 被认为是同一个域名组,共享一个组限制。

当磁盘空间告罄,quota manager 会开始删除临时存储 (temporary storage),以域名为单位删除,直到觉得磁盘空间余量尚足。

删除顺序遵循 LRU 原则,最后访问时间距离现在最远的域名下的 object store 会被整个删除。

目前 chrome (79) 的策略细节可能不太一样,但还没有找到具体文档描述。

查询当前存储空间状况

使用 StorageManager::estimate() 方法查询当前存储空间的配额 (quota) 和已使用量 (usage)。

详细描述在 Stroage 草案 中可查。

navigator.storage.estimate().then(function(estimate) {
  document.getElementById("percent").innerHTML =
      (estimate.usage / estimate.quota * 100).toFixed(2);
});

额外一说,在 Chrome 中返回的 estimate 还会多出一个非标准字段 usageDetails,能看到更详细的信息。

  "usageDetails": {
    "caches": 197632,
    "indexedDB": 187255711,
    "serviceWorkerRegistrations": 1561632
  }

实际使用时,一些技术选择考量

存储大文件和数据块

IndexedDB 在 WebApp 中可以担任数据持久层,比起更早的基于字符串的 LocalStorage ,可以存储更复杂、更多样化和更大的数据。以下是一些常见的场景:

  • ArrayBuffer/Uint8Array/Float32Array 存储等二进制数据和视图对象,用作密集计算
  • ImageData 在基于 canvas 的绘图程序或游戏中做绘制结果缓存
  • File/Blob 文件分片上传,大文件存储

Blob 与常用二进制数据类型互转

如果文件很大,建议使用 File/Blob,目前在 mac chrome 79 上测试,保存包含 ArrayBuffer 的单条记录超过 100MB,便会写入失败。

使用 Blob(或者基于它的 File 对象) 还有许多好处,如果应用的主要功能是音视频相关处理,使用 URL.createObjectURL(blob) 生成的字符串最后能直接作为原生 DOM 的 img/video/audio 标签接受的 src。而且 blob.type 是有意义的 MIME Type,在调试数据的时候也有更多的信息。因此 Blob 很适合作为处理后可以直接展示的成品存储。

但若数据需要被修改和操作,Blob 需要转化为 ArrayBuffer 以及具体的类型数据视图 Uint8Array 等使用。

Blob -> ArrayBuffer

以下两种方法,都为异步过程

1 使用 blob.arrayBuffer() 方法。此方法为标准的推荐,不过目前浏览器支持有限。

const arrayBuffer = awiat blob.arrayBuffer()

2 使用 FileReader

let arrayBuffer
const fr = new FileReader()
fr.onload = function(e) {
  arrayBuffer = e.target.value
}
fr.readAsArrayBuffer(blob)

ArrayBuffer -> Blob

同步过程

const blob = new Blob([arrayBuffer], { type: 'video/mp4' })

参考

https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria

https://developers.google.com/web/updates/2016/06/persistent-storage

https://developer.chrome.com/apps/offline_storage

https://stackoverflow.com/questions/17809824/how-to-use-navigator-instead-of-window-webkitstorageinfo-html5-file-system-api