Szacowanie dostępnego miejsca

tl;dr

Chrome w wersji 61 (w przyszłości dołączą do niego inne przeglądarki) wyświetla teraz szacunkowe informacje o tym, ile miejsca zajmuje aplikacja internetowa i ile miejsca jest jeszcze dostępne. Informacje te można wyświetlić:

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 przechowywanie danych

Gdy rozważasz potrzeby związane z przechowywaniem danych w nowoczesnych aplikacjach internetowych, warto podzielić co jest przechowywane na 2 kategorie: podstawowe dane potrzebne do załadowania aplikacji internetowej i dane potrzebne do efektywnej interakcji użytkownika z aplikacją po jej załadowaniu.

Pierwszy typ danych, czyli to, czego potrzeba do wczytania aplikacji internetowej, składa się z HTML, JavaScript, CSS i być może kilku obrazów. Usługi w tle wraz z interfejsem Cache Storage API zapewniają niezbędną infrastrukturę do zapisywania tych podstawowych zasobów, a następnie do ich używania w celu 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 lub starsze sw-precache, mogą w pełni zautomatyzować proces przechowywania, aktualizowania i używania tego typu danych.

A co z innymi typami danych? Są to zasoby, które nie są potrzebne do załadowania aplikacji internetowej, ale mogą odgrywać kluczową rolę w ogólnej wygodzie użytkownika. Jeśli na przykład piszesz aplikację internetową do edycji obrazów, możesz chcieć zapisać co najmniej 1 kopię lokalną obrazu, aby umożliwić użytkownikom przełączanie się między wersjami i cofanie zmian. Jeśli natomiast tworzysz aplikację do odtwarzania multimediów offline, zapisywanie plików audio lub wideo lokalnie będzie kluczową funkcją. Każda aplikacja internetowa, która może być personalizowana, musi zapisywać pewne informacje o stanie. Jakie jest dostępne miejsce na dane w ramach tego typu pamięci? Co się dzieje, gdy zabraknie miejsca?

Wcześniej: window.webkitStorageInfonavigator.webkitTemporaryStorage

Przeglądarki od dawna obsługują ten typ autorefleksji za pomocą interfejsów z prefiksem, takich jak bardzo stary (i wycofany) window.webkitStorageInfo oraz nie tak stary, ale nadal niestandardowy navigator.webkitTemporaryStorage. Chociaż te interfejsy dostarczały przydatnych informacji, nie mają przyszłości jako standardy internetowe.

Właśnie w tym miejscu pojawia się standard WHATWG Storage.

Przyszłość: navigator.storage

W ramach trwających prac nad Storage Living Standard do interfejsu StorageManager dodano kilka przydatnych interfejsów API, które są dostępne dla przeglądarek jako navigator.storage. Podobnie jak wiele innych nowszych interfejsów API sieciowych, navigator.storage jest dostępny tylko w przypadku źródeł zabezpieczonych (obsługiwanych przez HTTPS lub localhost).

W zeszłym roku wprowadziliśmy metodę navigator.storage.persist(), która umożliwia Twojej aplikacji internetowej wysłanie żądania wyłączenia jej magazynu z automatycznego czyszczenia.

Dołączyła do niej metoda navigator.storage.estimate(), która jest nowoczesną wersją metody navigator.webkitTemporaryStorage.queryUsageAndQuota(). estimate() zwraca podobne informacje, ale korzysta z interfejsu opartego na obietnicach, który jest zgodny z innymi nowoczesnymi interfejsami asynchronicznymi. Obietnica zwracana przez funkcję estimate() jest obiektem zawierającym 2 właściwości: usage, która reprezentuje liczbę bajtów obecnie używanych, oraz quota, która reprezentuje maksymalną liczbę bajtów, które mogą być przechowywane przez bieżącą źródło. (podobnie jak w przypadku innych elementów związanych z miejscem na dane, limit jest stosowany w całości źródła).

Jeśli aplikacja internetowa próbuje zapisać dane, które są wystarczająco duże, aby przekroczyć dostępną dla danego źródła kwotę, na przykład za pomocą IndexedDB lub interfejsu Cache Storage API, żądanie zakończy się niepowodzeniem z wyjątkiem QuotaExceededError.

Szacowanie miejsca na dane w akcji

Sposób użycia estimate() zależy od typu danych, które aplikacja musi przechowywać. Możesz na przykład zaktualizować element sterujący w interfejsie, aby poinformować użytkowników, ile miejsca jest używane po zakończeniu każdej operacji związanej z przechowywaniem. W idealnej sytuacji należy wtedy udostępnić interfejs, który pozwoli użytkownikom ręcznie usunąć niepotrzebne już dane. Możesz napisać kod w ten sposób:

// 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ładny jest ten szacowany wynik?

Nietrudno zauważyć, że dane zwracane przez funkcję to tylko oszacowanie miejsca zajmowanego przez źródło. Jest w nazwie funkcji. Wartości usage ani quota nie są stabilne, dlatego zalecamy uwzględnienie tych kwestii:

  • usage odzwierciedla liczbę bajtów, których dane z danego źródła używają do danych z tego samego źródła. Na tę liczbę mogą mieć wpływ wewnętrzne techniki kompresji, bloki alokacji o stałym rozmiarze, które mogą zawierać niewykorzystane 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 różnych domenach mogą dodać dodatkowe bajty wypełniające do ogólnej wartości usage.
  • quota odzwierciedla ilość miejsca obecnie zarezerwowanego dla źródła. Wartość ta zależy od pewnych stałych czynników, takich jak ogólny rozmiar pamięci, ale także od wielu potencjalnie zmiennych czynników, w tym od ilości miejsca na dane, które jest obecnie niewykorzystane. Gdy inne aplikacje na urządzeniu zapisują lub usuwają dane, ilość miejsca, którą przeglądarka jest w stanie przeznaczyć na dane źródłowe aplikacji internetowej, może się zmienić.

Obecna sytuacja: wykrywanie funkcji i zastępcze rozwiązania

estimate() jest domyślnie włączona od wersji 61 Chrome. Firefox eksperymentuje z funkcją navigator.storage, ale w sierpniu 2017 r. nie była ona domyślnie włączona. Aby przetestować tę funkcję, musisz włączyć ustawienie dom.storageManager.enabled.

Jeśli korzystasz z funkcji, która nie jest jeszcze obsługiwana we wszystkich przeglądarkach, konieczne jest wykrywanie funkcji. Możesz połączyć wykrywanie funkcji z opakowaniem opartym na obietnicy na podstawie starszych metod navigator.webkitTemporaryStorage, aby zapewnić spójny interfejs na przykład:

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