Stima dello spazio di archiviazione disponibile

tl;dr

Chrome 61, a cui seguiranno altri browser, ora mostra una stima di quanto lo spazio di archiviazione utilizzato da un'app web e la quantità disponibile tramite:

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

App web moderne e archiviazione dati

Quando si pensa alle esigenze di archiviazione di una moderna applicazione web, è utile avere suddividere cosa viene archiviato in due categorie: i dati di base necessari per caricare l'applicazione web e i dati necessari per un'interazione utente significativa l'applicazione viene caricata.

Il primo tipo di dati, quello necessario per caricare l'app web, è costituito da HTML, JavaScript, CSS e forse anche alcune immagini. Service worker, insieme a l'API Cache Storage, l'infrastruttura necessaria per salvare le risorse principali e quindi successivamente per caricare rapidamente la tua applicazione web, idealmente bypassando del tutto la rete. (gli strumenti che si integrano con il processo di creazione della tua applicazione web, come il nuovo Librerie Workbox o versioni precedenti sw-precache, automatizzare completamente il processo di archiviazione, aggiornamento e utilizzo data.)

E per quanto riguarda l'altro tipo di dati? Si tratta di risorse che non sono necessarie caricare la tua app web, ma che potrebbero svolgere un ruolo cruciale per l'utente in generale un'esperienza senza intervento manuale. Se devi scrivere un'app web per l'editing delle immagini, ad esempio, potresti vuoi salvare una o più copie locali di un'immagine, consentendo agli utenti di tra una revisione e l'altra e annullano il loro lavoro. Se stai sviluppando un media offline l'esperienza di riproduzione, salvare i file audio o video in locale sarebbe funzionalità. Ogni app web che può essere personalizzata finisce per dover salvare un po' una sorta di informazioni sullo stato. Come fai a sapere quanto spazio è disponibile per questo tipo di spazio di archiviazione del runtime, e cosa succede quando esaurisci lo spazio disponibile?

Il passato: window.webkitStorageInfo e navigator.webkitTemporaryStorage

Storicamente i browser supportavano questo tipo di introspezione tramite prefisso come l'interfaccia molto vecchia (e deprecata) window.webkitStorageInfo, e i sistemi non sono abbastanza vecchi, ma comunque non standard navigator.webkitTemporaryStorage. Queste interfacce forniscono informazioni utili, ma non dispongono di una il futuro come standard web.

È qui che lo standard di archiviazione WHATWG i dati inseriti nell'immagine.

Il futuro: navigator.storage

Nell'ambito del costante lavoro su Storage Living Standard, un paio di utili API hanno reso al StorageManager: esposta ai browser navigator.storage. Come molte altre API web più recenti, navigator.storage è disponibile solo su pagine sicure (pubblicate tramite HTTPS o localhost).

L'anno scorso abbiamo introdotto il navigator.storage.persist(): che consente alla tua applicazione web di richiedere che l'archiviazione venga esenti dalla pulizia automatica.

Ora è unito dal metodo navigator.storage.estimate(), che funge da sostituzione moderna di navigator.webkitTemporaryStorage.queryUsageAndQuota(). estimate() restituisce informazioni simili, ma espone una un'interfaccia basata su promessa, in linea con le altre moderne API asincrone. La promessa che estimate() restituisce le risoluzioni con un oggetto contenente due proprietà: usage, che rappresenta il numero di byte attualmente utilizzati e quota, che rappresenta il numero massimo di byte che possono essere archiviati origin. (Come per tutto ciò che riguarda lo spazio di archiviazione, la quota viene applicata all'intera origin.)

Se un'applicazione web tenta di archiviare, utilizzando, ad esempio, IndexedDB o API Cache Storage: dati abbastanza grandi da trasferire una determinata origine quota disponibile, la richiesta avrà esito negativo e QuotaExceededError .

Stime sullo spazio di archiviazione in azione

Il modo esatto in cui utilizzi estimate() dipende dal tipo di dati di cui la tua app ha bisogno . Ad esempio, puoi aggiornare un controllo nell'interfaccia in modo che gli utenti sapere quanto spazio viene utilizzato al termine di ogni operazione di archiviazione. Idealmente, quindi, è preferibile fornire un'interfaccia che consenta agli utenti di eseguire manualmente la pulizia dei dati che non serve più. Potresti scrivere il codice sulla base di:

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

Quanto è precisa la stima?

È difficile dimenticare che i dati che ottieni dalla funzione siano solo una stima dello spazio utilizzato da un'origine. È proprio lì nella funzione nome utente. Né i valori usage né i valori quota devono essere stabili, quindi ti consigliamo di tenere conto di quanto segue:

  • usage riflette la quantità di byte utilizzata efficacemente da una determinata origine stessa origine che a sua volta possono essere influenzati da tecniche di compressione interna blocchi di allocazione di dimensioni fisse che potrebbero includere spazio inutilizzato e la presenza di "tombstone" record che potrebbero essere create temporaneamente dopo un'eliminazione. Per evitare la perdita informazioni su dimensioni esatte, multiorigine, risorse opache il salvataggio in locale può contribuire a una spaziatura interna aggiuntiva in byte per il totale di usage valore.
  • quota riflette la quantità di spazio attualmente riservata per un'origine. La dipende da alcuni fattori costanti, come le dimensioni complessive dello spazio di archiviazione, ma anche di fattori potenzialmente volatili, inclusa la quantità di spazio di archiviazione che al momento non è utilizzato. Le altre applicazioni su un dispositivo scrivono o eliminano la quantità di spazio che il browser è disposto a dedicare al tuo è probabile che l'origine dell'app cambi.

Presente: rilevamento delle funzionalità e fallback

estimate() viene attivato per impostazione predefinita a partire da Chrome 61. Firefox è sperimentando con navigator.storage, ma da agosto 2017 non è stato per impostazione predefinita. Devi attiva la preferenza dom.storageManager.enabled per testarla.

Quando lavori con funzionalità non ancora supportate in tutti i browser, il rilevamento delle caratteristiche è indispensabile. Puoi combinare il rilevamento delle caratteristiche con una wrapper basato sulla promessa oltre al precedente navigator.webkitTemporaryStorage per fornire un'interfaccia coerente sulla stessa linea:

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