跳转到内容

存储 · API

StorageManager.estimate()

发布于

navigator.storage.estimate() 解析为两个字节数:当前源已经占用的 usage,以及它最多可以占用的保守上限 quota。上限是浏览器在调用时按磁盘比例算出来的,不是常量,所以只能在运行时读取,不能写死在代码里。

navigator.storage.estimate()

该方法不接受参数,返回一个以 StorageEstimate 字典兑现的 Promise。窗口和 worker(Service Worker 内为 self.navigator.storage)都可以调用,仅限安全上下文(BCD api.StorageManager.estimate:Chrome 61、Firefox 57、Safari 17)。

无。

Promise 兑现为一个对象,含两个标准成员和一个 Chromium 扩展成员。

成员 类型 含义
usage number 该源在受配额管理的存储(Cache Storage、IndexedDB、OPFS、Service Worker 注册)中占用的字节数。经过取整和填充,不是精确账目。
quota number 该源总共可用的字节数。保守估计,随磁盘填满而缩小。
usageDetails object 仅 Chromium(BCD api.StorageManager.estimate.usageDetails)。按存储系统细分的 usage;用量为 0 的系统会被省略,读取前先判断键是否存在。

Storage 标准允许浏览器用压缩、去重和填充对这两个数字做混淆,避免页面从精确字节数推断磁盘容量或其他站点的数据。Chromium 对不透明响应的填充最为明显:一条几 KB 的跨源 no-cors 响应会让 usage 增加约 7 MB(Understanding storage quota,developer.chrome.com)。

每个引擎都从磁盘容量推导 quota,但公式不同(Storage quotas and eviction criteria,developer.mozilla.org)。

引擎 每源配额 所有源的总上限
Chromium(Chrome、Edge) 磁盘总容量的 60%,尽力而为与持久化模式相同。无痕模式降到约 5%;开启「关闭所有窗口时清除 Cookie 和网站数据」后上限约 300 MB(Storage for the web,web.dev)。 磁盘总容量的 80%
Firefox 磁盘总容量的 10% 与 10 GiB 分组上限(同一站点 eTLD+1 下所有源共享)两者取小。持久化源可用磁盘的 50%,上限 8 TiB,且不受分组上限约束。 可用磁盘的 50%
WebKit(Safari 17、iOS 17、macOS 14) 浏览器应用内约为磁盘总容量的 60%,其他应用的 WKWebView 内约 15%;跨源 frame 得到嵌入方配额的 10%。添加到主屏幕或 Dock 的 Web App 沿用浏览器的数值(Updates to Storage Policy,webkit.org)。 浏览器应用内为磁盘的 80%,其他应用内为 20%

Safari 17 之前,每个源起始配额为 1 GiB,用满后 Safari 询问用户并按 200 MB 递增。旧版 WebKit 报告的 quota 会在提示之后变化,这也是每次大写入前重读而不要缓存它的又一个理由。

异常 触发条件
TypeError 浏览器无法为该源获取存储架:源是不透明源(没有 allow-same-origin 的沙箱 <iframe>、data: 文档),或用户已为该站点禁用存储。Promise 被拒绝,方法本身不抛出。

空间不足不是 estimate() 的异常。超过 quota 的写入会在执行写入的存储里失败:cache.put() 以 QuotaExceededError DOMException 拒绝,IndexedDB 请求触发 error 事件、request.error.name === "QuotaExceededError" 并中止事务。

三个示例分别用于展示用量、在大写入前决策,以及在方法缺失时降级。

两个成员足以显示该源用了多少。先做除法再格式化即可,两者都是字节数,普通浮点运算足够精确。

const { usage, quota } = await navigator.storage.estimate();
const percent = ((usage / quota) * 100).toFixed(1);
console.log(`${(usage / 1048576).toFixed(1)} MiB / ${(quota / 1073741824).toFixed(1)} GiB(${percent}%)`);

在 Chromium 的 60% 规则下,桌面机的 quota 通常是几百 GB;接近满盘的手机则小得多。适合一台设备的预算不一定适合另一台。

在已知大小的写入之前立即调用 estimate(),并为上文的填充留出余量。写入仍然失败时,捕获 QuotaExceededError,释放空间后重试一次,而不是让整个操作失败。

async function cacheLargeAsset(cache, url, expectedBytes) {
const { usage, quota } = await navigator.storage.estimate();
if (quota - usage < expectedBytes * 1.2) {
await pruneOldEntries(cache);
}
try {
await cache.add(url);
} catch (err) {
if (err.name !== 'QuotaExceededError') throw err;
await pruneOldEntries(cache);
await cache.add(url);
}
}

20% 的余量是针对取整的经验值,不来自任何规范;真正重要的是清理后的重试,因为一秒前读到的 quota 在一台正在同时下载照片的设备上已经过期。

不安全源上没有 navigator.storage,早于「语法」一节所列版本的浏览器也没有。把方法缺失当作「预算未知」,乐观写入并依赖存储自身的 QuotaExceededError。

async function storageBudget() {
if (!('storage' in navigator) || typeof navigator.storage.estimate !== 'function') {
return null; // 预算未知:退回到在写入时捕获配额错误
}
try {
return await navigator.storage.estimate();
} catch (err) {
if (err.name === 'TypeError') return null; // 不透明源或存储被禁用
throw err;
}
}

返回 null 让调用方只走一条路径:尝试写入并处理失败。这件事它本来就必须做,因为估计值只是参考。

规范

规范状态
无。