估算可用儲存空間

tl;dr

Chrome 61 支援更多瀏覽器,現在可透過以下方式估算網頁應用程式使用的儲存空間及可用儲存空間:

if ('storage' in navigator && 'estimate' in navigator.storage) {
  navigator.storage.estimate().then(({usage, quota}) => {
    console.log(`Using ${usage} out of ${quota} bytes.`);
  });
}

新式網頁應用程式和資料儲存空間

當您思考新型網頁應用程式的儲存空間需求時,可以將「哪些內容」分為兩類:載入網頁應用程式所需的核心資料,以及應用程式載入後進行有意義的使用者互動所需的資料。

第一種是載入網頁應用程式需要的資料類型,由 HTML、JavaScript、CSS 和一些圖片組成。服務工作站Cache Storage API 都提供儲存核心資源所需的基礎架構,稍後使用這些基礎架構來快速載入網頁應用程式,最好能完全略過網路。(與網頁應用程式建構程序整合的工具 (例如新的 Workbox 程式庫或舊版 sw-precache) 可以完全自動化處理儲存、更新及使用這類資料的程序。

但其他類型的資料呢?這些資源不是載入網頁應用程式的必要資源,不過這在整體使用者體驗中可能發揮重要作用。舉例來說,如果您編寫的是圖片編輯網頁應用程式,可以儲存一或多個圖片的本機副本,讓使用者切換不同修訂版本並復原其工作。或者,如果您開發離線媒體播放體驗,將音訊或影片檔案儲存在本機會十分重要。每個可以個人化的網頁應用程式,最後都需要儲存某些「狀態資訊」。如何知道此類型的執行階段儲存空間還有多少可用空間?空間用盡時會發生什麼情況?

過去:window.webkitStorageInfonavigator.webkitTemporaryStorage

瀏覽器過去是透過帶有前置字串的介面 (例如非常舊和已淘汰的 window.webkitStorageInfo) 以及不等舊但仍非標準的 navigator.webkitTemporaryStorage 等介面,支援這種自我檢查。雖然這些介面提供實用資訊,但並非與網路標準一樣。

這時 WHATWG Storage Standard 即進入圖片位置。

未來趨勢:navigator.storage

為持續進行 Storage Living Standard 工作,StorageManager 介面已提供幾種實用的 API,而瀏覽器會以 navigator.storage 的形式公開這個介面。如同其他許多較新的網路 API,navigator.storage 只能在安全 (透過 HTTPS 或 localhost) 來源中提供

我們在去年推出 navigator.storage.persist() 方法,讓網頁應用程式可以要求不要自動清理儲存空間。

現在可以透過 navigator.storage.estimate() 方法彙整,做為 navigator.webkitTemporaryStorage.queryUsageAndQuota() 的現代化替代方案。estimate() 會傳回類似資訊,但會顯示以承諾為基礎的介面,其將與其他現代非同步 API 保持一致。承諾 estimate() 傳回的物件會包含兩個屬性:usage (代表目前使用的位元組數) 和 quota (代表目前來源可儲存的位元組數上限)。(如同其他與儲存空間相關的設定,配額會套用至整個來源)。

如果網頁應用程式嘗試儲存 (例如使用 IndexedDB 或 Cache Storage API) 儲存指定來源超過其可用配額的資料,該要求就會失敗,並顯示 QuotaExceededError 例外狀況。

儲存空間預估實際運作情形

使用 estimate() 的確切方式取決於應用程式需要儲存的資料類型。舉例來說,您可以更新介面中的控制項,讓使用者瞭解每項儲存空間作業完成後已使用多少空間。在理想情況下,建議您提供一個介面,讓使用者手動清除不再需要的資料。您可能會依序編寫以下這幾行程式碼:

// For a primer on async/await, see
// https://developers.google.com/web/fundamentals/getting-started/primers/async-functions
async function storeDataAndUpdateUI(dataUrl) {
  // Pro-tip: The Cache Storage API is available outside of service workers!
  // See https://googlechrome.github.io/samples/service-worker/window-caches/
  const cache = await caches.open('data-cache');
  await cache.add(dataUrl);

  if ('storage' in navigator && 'estimate' in navigator.storage) {
    const {usage, quota} = await navigator.storage.estimate();
    const percentUsed = Math.round(usage / quota * 100);
    const usageInMib = Math.round(usage / (1024 * 1024));
    const quotaInMib = Math.round(quota / (1024 * 1024));

    const details = `${usageInMib} out of ${quotaInMib} MiB used (${percentUsed}%)`;

    // This assumes there's a <span id="storageEstimate"> or similar on the page.
    document.querySelector('#storageEstimate').innerText = details;
  }
}

系統估計值的準確度如何?

因此很難遺漏,因為您從函式傳回的資料只是來源使用空間的估計值。就在函式名稱中!usagequota 值都不是固定不變,因此建議您考量下列事項:

  • usage 反映了指定來源有效使用相同來源資料多少位元組數,而這些位元組可能會受到內部壓縮技術的影響、可能包含未使用空間的固定大小配置區塊,以及可能在刪除後暫時建立的「tombstone」記錄。為避免確切大小資訊外洩,跨來源儲存的不透明資源可能會為整體 usage 值產生額外的邊框間距位元組。
  • quota 反映目前為來源預留的空間大小。這個值取決於一些常數因素,例如整體儲存空間大小,以及一些潛在的波動因素,包括目前未使用的儲存空間量。因此,裝置上的其他應用程式會寫入或刪除資料,瀏覽器願意投注於網頁應用程式來源的空間,就可能會改變。

簡報內容:功能偵測和備用

從 Chrome 61 版開始,estimate()會預設為啟用。Firefox 正透過 navigator.storage 進行實驗,但自 2017 年 8 月起,它預設為啟用。您必須啟用 dom.storageManager.enabled 偏好設定,才能進行測試。

如果您使用某些瀏覽器尚未支援的功能,就必須使用功能偵測。您可以在舊版 navigator.webkitTemporaryStorage 方法的基礎上,結合功能偵測與以入侵為基礎的包裝函式,在以下幾行提供一致的介面:

function storageEstimateWrapper() {
  if ('storage' in navigator && 'estimate' in navigator.storage) {
    // We've got the real thing! Return its response.
    return navigator.storage.estimate();
  }

  if ('webkitTemporaryStorage' in navigator &&
      'queryUsageAndQuota' in navigator.webkitTemporaryStorage) {
    // Return a promise-based wrapper that will follow the expected interface.
    return new Promise(function(resolve, reject) {
      navigator.webkitTemporaryStorage.queryUsageAndQuota(
        function(usage, quota) {resolve({usage: usage, quota: quota})},
        reject
      );
    });
  }

  // If we can't estimate the values, return a Promise that resolves with NaN.
  return Promise.resolve({usage: NaN, quota: NaN});
}