Szacowanie dostępnego miejsca

tl;dr

Chrome 61 (wkrótce wraz z kolejnymi przeglądarkami) pokazuje teraz szacunkową ilość miejsca na dane wykorzystywanego przez aplikację internetową oraz za pomocą:

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

Nowoczesne aplikacje internetowe i miejsce na dane

Gdy myślisz o potrzebach w zakresie miejsca na dane w nowoczesnej aplikacji internetowej, warto podzielić to, co jest przechowywane, na 2 kategorie: podstawowe dane potrzebne do wczytania aplikacji internetowej i dane potrzebne do efektywnej interakcji z użytkownikiem po jej wczytaniu.

Pierwszy typ danych, który jest niezbędny do wczytania aplikacji internetowej, składa się z HTML, JavaScriptu, CSS i być może nawet kilku obrazów. Skrypty service worker wraz z interfejsem Cache Storage API zapewniają infrastrukturę niezbędną do zapisywania tych podstawowych zasobów, a następnie wykorzystują je do szybkiego wczytywania aplikacji internetowej, najlepiej całkowicie omiając sieć. Narzędzia, które integrują się z procesem kompilacji aplikacji internetowej, takie jak nowe biblioteki Workbox lub starsza wersja sw-precache, mogą w pełni zautomatyzować proces przechowywania, aktualizowania i wykorzystywania danych tego typu.

A co z innym rodzajem danych? Są to zasoby, które nie są potrzebne do wczytywania aplikacji internetowej, ale mogą mieć kluczowe znaczenie dla wygody użytkowników. W przypadku tworzenia aplikacji internetowej do edycji obrazów możesz na przykład zapisać jedną lub więcej lokalnych kopii obrazu, aby użytkownicy mogli przełączać się między wersjami i cofać wykonaną pracę. Jeśli tworzysz odtwarzanie multimediów offline, zapisywanie plików audio i wideo lokalnie będzie kluczową funkcją. Każda aplikacja internetowa, którą można spersonalizować, musi zapisywać informacje o stanie. Jak sprawdzić, ile miejsca jest dostępne na potrzeby tego typu środowiska wykonawczego i co się stanie, gdy zabraknie Ci miejsca?

Przeszłość: window.webkitStorageInfo i navigator.webkitTemporaryStorage

Przeglądarki w przeszłości obsługiwały ten typ introspekcji za pomocą interfejsów z prefiksami, np. bardzo starych (i wycofanych) window.webkitStorageInfo oraz niezbyt starych, ale wciąż niestandardowych navigator.webkitTemporaryStorage. Choć interfejsy te dostarczały przydatnych informacji, to nie jest w przyszłości tak ambitna jak standardy internetowe.

W tym miejscu znajduje się standard WhatWG Storage Standard.

Przyszłość: navigator.storage

W ramach trwających prac nad Storage Living Standard wprowadziliśmy kilka przydatnych interfejsów API w interfejsie StorageManager, który jest dostępny dla przeglądarek jako navigator.storage. Podobnie jak wiele innych nowszych internetowych interfejsów API, navigator.storage jest dostępny tylko w bezpiecznych źródłach (udostępnianych za pomocą protokołu HTTPS lub lokalnego hosta).

W zeszłym roku wprowadziliśmy metodę navigator.storage.persist(), która umożliwia aplikacji internetowej żądanie zwolnienia jej miejsca na dane z automatycznego czyszczenia.

Jest teraz połączona metodą navigator.storage.estimate(), która służy jako nowoczesny zamiennik metody navigator.webkitTemporaryStorage.queryUsageAndQuota(). estimate() zwraca podobne informacje, ale ujawnia interfejs oparty na obietnicach, co jest zgodne z innymi nowoczesnymi asynchronicznymi interfejsami API. Obietnica, że funkcja estimate() zwracana jest w obiekcie zawierającym 2 właściwości: usage, która odpowiada obecnie liczbie używanych bajtów, oraz quota, która reprezentuje maksymalną liczbę bajtów, które mogą być przechowywane w bieżącej źródle. (Podobnie jak w przypadku wszystkich innych kwestii związanych z miejscem na dane, limit jest stosowany do całej domeny).

Jeśli aplikacja internetowa spróbuje przechowywać (na przykład przy użyciu IndexedDB lub interfejsu Cache Storage API) dane, które są wystarczająco duże, aby przenieść dany punkt początkowy na dostępny limit, żądanie zakończy się niepowodzeniem z wyjątkiem QuotaExceededError.

Szacowane miejsce na dane w praktyce

To, w jaki sposób używasz estimate(), zależy od typu danych, jakie musi przechowywać aplikacja. Możesz na przykład zaktualizować element sterujący w interfejsie, aby użytkownicy wiedzieli, ile miejsca wykorzystuje się po zakończeniu każdej operacji na dane. Najlepiej jest udostępnić interfejs umożliwiający użytkownikom ręczne usuwanie niepotrzebnych danych. Możesz napisać kod podobny do tego:

// 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;
  }
}

Jak dokładne są te szacunki?

Trudno pominąć fakt, że dane zwracane przez funkcję to tylko oszacowanie przestrzeni używanej przez źródło. Te dane są właśnie w nazwie funkcji. Ani wartości usage, ani quota nie powinny być stabilne, więc zalecamy, aby wziąć pod uwagę te kwestie:

  • usage pokazuje, ile bajtów faktycznie wykorzystuje dane źródło na potrzeby danych tego samego pochodzenia, na które z kolei mogą wpływać wewnętrzne techniki kompresji, bloki przydziału o stałym rozmiarze, które mogą obejmować niewykorzystaną przestrzeń, oraz obecność rekordów „tombstone”, które mogą być tworzone tymczasowo po usunięciu danych. Aby zapobiec wyciekowi informacji o dokładnym rozmiarze, nieprzejrzyste zasoby zapisane lokalnie mogą dodawać dodatkowe bajty dopełnienia do ogólnej wartości usage.
  • quota odzwierciedla ilość miejsca zarezerwowanego obecnie dla źródła. Ta wartość zależy od pewnych stałych czynników, takich jak ogólny rozmiar miejsca na dane, ale też od szeregu czynników potencjalnie zmiennych, takich jak ilość nieużywanego obecnie miejsca na dane. W miarę jak inne aplikacje na urządzeniu zapisują lub usuwają dane, ilość miejsca, jaką przeglądarka może przeznaczyć na pochodzenie Twojej aplikacji internetowej, prawdopodobnie się zmieni.

Teraz: wykrywanie funkcji i ich wartości zastępcze

Od wersji Chrome 61 usługa estimate() jest domyślnie włączona. W Firefoksie eksperymentujemy z usługą navigator.storage, ale od sierpnia 2017 r. nie jest ona domyślnie włączona. Aby przetestować ustawienie dom.storageManager.enabled, musisz je włączyć.

Podczas pracy z funkcjami, które nie są jeszcze obsługiwane we wszystkich przeglądarkach, konieczne jest wykrywanie funkcji. Możesz połączyć wykrywanie funkcji z kodem opartym na obietnicy w połączeniu ze starszymi metodami navigator.webkitTemporaryStorage, aby uzyskać spójny interfejs:

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});
}