Beschikbare opslagruimte schatten

tl;dr

Chrome 61, en er zullen nog meer browsers volgen, geeft nu een schatting van hoeveel opslagruimte een webapp gebruikt en hoeveel beschikbaar is via:

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

Moderne webapps en gegevensopslag

Wanneer u nadenkt over de opslagbehoeften van een moderne webapplicatie, helpt het om de opslag in twee categorieën op te delen: de kerngegevens die nodig zijn om de webapplicatie te laden, en de gegevens die nodig zijn voor zinvolle gebruikersinteractie zodra de applicatie is geladen.

Het eerste type gegevens, wat nodig is om uw web-app te laden, bestaat uit HTML, JavaScript, CSS en misschien enkele afbeeldingen. Servicemedewerkers bieden, samen met de Cache Storage API , de benodigde infrastructuur voor het opslaan van deze kernbronnen en deze later te gebruiken om uw webapp snel te laden, waarbij idealiter het netwerk volledig wordt omzeild. (Tools die integreren met het bouwproces van uw web-app, zoals de nieuwe Workbox- bibliotheken of de oudere sw-precache , kunnen het proces van het opslaan, bijwerken en gebruiken van dit soort gegevens volledig automatiseren.)

Maar hoe zit het met het andere type gegevens? Dit zijn bronnen die niet nodig zijn om uw web-app te laden, maar die mogelijk een cruciale rol spelen in uw algehele gebruikerservaring. Als u bijvoorbeeld een webapp voor het bewerken van afbeeldingen schrijft, wilt u misschien een of meer lokale kopieën van een afbeelding opslaan, zodat gebruikers tussen revisies kunnen schakelen en hun werk ongedaan kunnen maken. Of als u een offline media-afspeelervaring ontwikkelt, is het lokaal opslaan van audio- of videobestanden een cruciale functie. Elke web-app die kan worden gepersonaliseerd, moet uiteindelijk een soort statusinformatie opslaan. Hoe weet u hoeveel ruimte er beschikbaar is voor dit type runtime-opslag en wat gebeurt er als u geen ruimte meer heeft?

Het verleden: window.webkitStorageInfo en navigator.webkitTemporaryStorage

Browsers hebben dit soort introspectie historisch ondersteund via vooraf ingestelde interfaces, zoals de zeer oude (en verouderde) window.webkitStorageInfo en de niet zo oude, maar nog steeds niet-standaard navigator.webkitTemporaryStorage . Hoewel deze interfaces nuttige informatie opleverden, hebben ze geen toekomst als webstandaarden.

Dat is waar de WHATWG-opslagstandaard in beeld komt.

De toekomst: navigator.storage

Als onderdeel van het voortdurende werk aan de Storage Living Standard zijn een aantal nuttige API's in de StorageManager interface terechtgekomen, die in browsers wordt weergegeven als navigator.storage . Net als veel andere nieuwere web-API's is navigator.storage alleen beschikbaar op een beveiligde (geserveerd via HTTPS of localhost) oorsprong.

Vorig jaar hebben we de methode navigator.storage.persist() geïntroduceerd , waarmee uw webapplicatie kan verzoeken dat de opslag wordt vrijgesteld van automatisch opschonen.

Het wordt nu vergezeld door de methode navigator.storage.estimate() , die dient als een moderne vervanging voor navigator.webkitTemporaryStorage.queryUsageAndQuota() . estimate() retourneert soortgelijke informatie, maar biedt een op beloften gebaseerde interface, die in overeenstemming is met andere moderne asynchrone API's. De belofte die estimate() retourneert, wordt opgelost met een object dat twee eigenschappen bevat: usage , dat het aantal bytes vertegenwoordigt dat momenteel wordt gebruikt, en quota , dat het maximale aantal bytes vertegenwoordigt dat kan worden opgeslagen door de huidige origin . (Net als al het andere dat met opslag te maken heeft, worden quota toegepast op de hele oorsprong.)

Als een webtoepassing gegevens probeert op te slaan (bijvoorbeeld met IndexedDB of de Cache Storage API) die groot genoeg zijn om een ​​bepaalde oorsprong boven het beschikbare quotum te brengen, mislukt het verzoek met een QuotaExceededError -uitzondering.

Opslagschattingen in actie

Hoe u estimate() precies gebruikt, hangt af van het type gegevens dat uw app moet opslaan. U kunt bijvoorbeeld een besturingselement in uw interface bijwerken, zodat gebruikers weten hoeveel ruimte er wordt gebruikt nadat elke opslagbewerking is voltooid. Idealiter zou je dan een interface bieden waarmee gebruikers handmatig gegevens kunnen opschonen die niet langer nodig zijn. Je zou code kunnen schrijven in de trant van:

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

Hoe nauwkeurig is de schatting?

Het is moeilijk te ontgaan dat de gegevens die u uit de functie terugkrijgt slechts een schatting zijn van de ruimte die een oorsprong in beslag neemt. Het staat precies in de functienaam! Noch het usage , noch de quota zijn bedoeld om stabiel te zijn. Het is daarom raadzaam rekening te houden met het volgende:

  • usage weerspiegelt hoeveel bytes een bepaalde oorsprong effectief gebruikt voor gegevens van dezelfde oorsprong , die op hun beurt kunnen worden beïnvloed door interne compressietechnieken, toewijzingsblokken met een vaste grootte die ongebruikte ruimte kunnen bevatten, en de aanwezigheid van "tombstone" -records die mogelijk zijn tijdelijk aangemaakt na een verwijdering. Om het lekken van informatie over de exacte grootte te voorkomen, kunnen ondoorzichtige bronnen die lokaal zijn opgeslagen, extra opvulbytes bijdragen aan de algehele usage .
  • quota weerspiegelt de hoeveelheid ruimte die momenteel is gereserveerd voor een oorsprong. De waarde is afhankelijk van een aantal constante factoren, zoals de totale opslaggrootte, maar ook van een aantal potentieel volatiele factoren, waaronder de hoeveelheid opslagruimte die momenteel ongebruikt is. Dus naarmate andere applicaties op een apparaat gegevens schrijven of verwijderen, zal de hoeveelheid ruimte die de browser bereid is te besteden aan de oorsprong van uw web-app waarschijnlijk veranderen.

Het heden: functiedetectie en fallbacks

estimate() is standaard ingeschakeld vanaf Chrome 61. Firefox experimenteert met navigator.storage , maar sinds augustus 2017 is dit niet standaard ingeschakeld. U moet de voorkeur dom.storageManager.enabled inschakelen om deze te kunnen testen.

Wanneer u werkt met functionaliteit die nog niet in alle browsers wordt ondersteund, is functiedetectie een must. U kunt functiedetectie combineren met een op belofte gebaseerde wrapper bovenop de oudere navigator.webkitTemporaryStorage methoden om een ​​consistente interface te bieden in de trant van:

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