Szacowanie dostępnego miejsca

tl;dr

W Chrome 61, a wkrótce pojawią się kolejne przeglądarki, teraz można sprawdzić, ile miejsca wykorzystuje aplikacja internetowa oraz ile jest dostępnych:

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

Jeśli weźmiemy pod uwagę potrzeby nowoczesnej aplikacji internetowej w zakresie miejsca na dane, warto podzielić przechowywane dane na 2 kategorie: podstawowe dane potrzebne do załadowania aplikacji internetowej i dane potrzebne do konstruktywnej interakcji z użytkownikiem po wczytaniu aplikacji.

Pierwszy typ danych, potrzebny do wczytania aplikacji internetowej, składa się z HTML, JavaScriptu, CSS i być może kilku obrazów. Skrypty Service Worker wraz z interfejsem Cache Storage API zapewniają infrastrukturę niezbędną do zapisania tych podstawowych zasobów, a następnie użycia ich do szybkiego wczytywania aplikacji internetowej, najlepiej całkowicie omijając sieć. (Narzędzia, które integrują się z procesem kompilacji aplikacji internetowej, takie jak nowe biblioteki Workbox i starsze biblioteki sw-precache, mogą w pełni zautomatyzować proces przechowywania, aktualizowania i używania tego typu danych).

A co z drugim rodzajem danych? Są to zasoby, które nie są potrzebne do wczytywania aplikacji internetowej, ale mogą mieć kluczowy wpływ na ogólne działanie usługi. Jeśli np. piszesz aplikację internetową do edycji obrazów, możesz zapisać jedną lub więcej lokalnych kopii obrazu, co pozwoli użytkownikom na przełączanie się między wersjami i cofanie wykonanych zadań. Z kolei, jeśli tworzysz treści do odtwarzania w trybie offline, zapisywanie lokalnie plików audio i wideo będzie kluczowe. Każda spersonalizowana aplikacja internetowa musi zapisać informacje stanowe. Jak sprawdzić, ile miejsca jest dostępne na ten typ pamięci środowiska wykonawczego i co się stanie, gdy skończy Ci się miejsce?

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

Przeglądarki do tej pory obsługiwały ten typ introspekcji za pomocą interfejsów z prefiksami, takich jak bardzo stary (i wycofany) window.webkitStorageInfo czy niezbyt stary, ale nadal niestandardowy navigator.webkitTemporaryStorage. Interfejsy te dostarczają przydatnych informacji, ale na pewno nie są dla nich normami internetowymi.

Właśnie tu pojawia się standard pamięci WhatWG.

Przyszłość: navigator.storage

W ramach prac nad Storage Living Standard wprowadziliśmy do interfejsu StorageManager kilka przydatnych interfejsów API, które są widoczne dla przeglądarek jako navigator.storage. Podobnie jak wiele innych nowszych interfejsów API, usługa navigator.storage jest dostępna tylko w bezpiecznych (udostępnianych przez HTTPS lub localhost).

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

Jest ona połączona z metodą navigator.storage.estimate(), która pełni funkcję nowoczesnego zamiennika metody navigator.webkitTemporaryStorage.queryUsageAndQuota(). estimate() zwraca podobne informacje, ale ujawnia interfejs oparty na obietnicach, który jest zgodny z innymi nowoczesnymi asynchronicznymi interfejsami API. Obietnica zwracania funkcji estimate() jest rozpatrywana przez obiekt zawierający 2 właściwości: usage reprezentującą aktualnie używaną liczbę bajtów oraz quota reprezentującą maksymalną liczbę bajtów, które mogą być przechowywane przez bieżące źródło. Tak jak w przypadku wszystkich innych danych związanych z miejscem na dane limit jest stosowany do całej domeny.

Jeśli aplikacja internetowa próbuje zapisać (np. przy użyciu IndexedDB lub interfejsu Cache Storage API) dane, które są wystarczająco duże, aby przenieść dane źródło ponad jego dostępny limit, żądanie zakończy się niepowodzeniem (wyjątek: QuotaExceededError).

Oszacowanie ilości miejsca na dane w praktyce

To, jak użyjesz estimate(), zależy od typu danych, które musi przechowywać aplikacja. Możesz na przykład zaktualizować w interfejsie element sterujący, który pozwoli użytkownikom sprawdzić, ile miejsca jest wykorzystywane po zakończeniu każdej operacji związanej z pamięcią. Najlepiej udostępnić interfejs, który pozwala użytkownikom ręcznie usuwać dane, które nie są już potrzebne. Możesz napisać kod w tych wierszach:

// 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 dane?

Trudno zapomnieć, że dane uzyskiwane z funkcji są tylko oszacowaniem przestrzeni używanej przez źródło. Znajdziesz go w nazwie funkcji. Ani wartości usage, ani quota nie są stabilne, dlatego zalecamy wziąć pod uwagę te kwestie:

  • usage pokazuje liczbę bajtów używanych przez dane źródło w przypadku danych z samego źródła, na które mogą wpływać wewnętrzne techniki kompresji, bloki alokacji o stałym rozmiarze, które mogą obejmować nieużywane miejsce, oraz obecność rekordów „tombstone”, które mogą zostać tymczasowo utworzone po usunięciu. Aby zapobiec wyciekowi informacji o dokładnym rozmiarze, nieprzezroczyste zasoby zapisane lokalnie w innych domenach mogą powodować dodatkowe bajty dopełnienia w 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 również od szeregu czynników zmiennych, takich jak ilość miejsca, które nie jest obecnie używane. Tak jak inne aplikacje na urządzeniu zapisują lub usuwają dane, więc ilość miejsca, jaką przeglądarka może przeznaczyć na źródło Twojej aplikacji, prawdopodobnie się zmieni.

Czas teraźniejszy: wykrywanie cech i wartości zastępcze

Od wersji Chrome 61 interfejs estimate() jest domyślnie włączony. Firefox eksperymentuje z funkcją navigator.storage, ale od sierpnia 2017 roku nie jest ona domyślnie włączona. Aby przetestować tę opcję, musisz włączyć ustawienie dom.storageManager.enabled.

W przypadku pracy z funkcjami, które nie są jeszcze obsługiwane we wszystkich przeglądarkach, niezbędne jest wykrywanie funkcji. Wykrywanie funkcji możesz połączyć z kodem opartym na obietnicach w uzupełnieniu starszych metod navigator.webkitTemporaryStorage, aby uzyskać spójny interfejs obejmujący:

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