Spazio di archiviazione per il Web

Esistono molte opzioni diverse per archiviare i dati nel browser. Quale è la soluzione più adatta alle tue esigenze?

Le connessioni a internet possono essere instabili o non esistenti quando sei in viaggio, motivo per cui il supporto offline e le prestazioni affidabili sono funzionalità comuni delle app web progressive. Anche in ambienti wireless perfetti, l'uso giudizioso della memorizzazione nella cache e di altre tecniche di archiviazione può migliorare notevolmente l'esperienza utente. Esistono diversi modi per memorizzare nella cache le risorse dell'applicazione statica (HTML, JavaScript, CSS, immagini e così via) e i dati (dati utente, articoli di notizie e così via). Ma qual è la soluzione migliore? Quanto puoi archiviare? Come faccio a evitare che venga espulso?

Che cosa devo usare?

Ecco un consiglio generale per l'archiviazione delle risorse:

IndexedDB, OPFS e l'API Cache Storage sono supportati in tutti i browser moderni. Sono asincroni e non bloccano il thread principale (ma esiste anche una variante sincrona dell'OPFS disponibile esclusivamente nei worker web). Sono accessibili dall'oggetto window, dai worker web e dai worker di servizio, il che consente di utilizzarli ovunque nel codice.

E per quanto riguarda altri meccanismi di archiviazione?

Esistono diversi altri meccanismi di archiviazione disponibili nel browser, ma hanno un utilizzo limitato e potrebbero causare problemi di prestazioni significativi.

SessionStorage è specifico per la scheda e limitato alla sua durata. Può essere utile per archiviare piccole quantità di informazioni specifiche della sessione, ad esempio una chiave IndexedDB. Deve essere utilizzato con cautela perché è sincrono e bloccherà il thread principale. È limitado a circa 5 MB e può contenere solo stringhe. Poiché è specifico per la scheda, non è accessibile da web worker o service worker.

LocalStorage deve essere evitato perché è sincrono e bloccherà il thread principale. È limitato a circa 5 MB e può contenere solo stringhe. LocalStorage non è accessibile da web worker o service worker.

I cookie hanno i loro utilizzi, ma non devono essere utilizzati per lo spazio di archiviazione. I cookie vengono inviati con ogni richiesta HTTP, pertanto la memorizzazione di più di una piccola quantità di dati aumenterà notevolmente le dimensioni di ogni richiesta web. Sono sincroni e non sono accessibili dai worker web. Come LocalStorage e SessionStorage, i cookie sono limitati solo alle stringhe.

L'API File System Access è stata progettata per consentire agli utenti di leggere e modificare i file sul file system locale. L'utente deve concedere l'autorizzazione prima che una pagina possa leggere o scrivere in un file locale e le autorizzazioni non vengono conservate nelle sessioni, a meno che un handle file non venga memorizzato nella cache in IndexedDB. L'API File System Access è più adatta per casi d'uso come gli editor, in cui è necessario aprire un file, modificarlo e, eventualmente, salvare nuovamente le modifiche al file.

L'API File System e l'API FileWriter forniscono metodi per leggere e scrivere file in un file system in sandbox. Sebbene sia asincrono, non è consigliato perché è disponibile solo nei browser basati su Chromium.

Quanto posso archiviare?

In breve, molto, almeno un paio di centinaia di megabyte e potenzialmente anche centinaia di gigabyte o più. Le implementazioni dei browser variano, ma la quantità di spazio di archiviazione disponibile si basa in genere sulla quantità di spazio di archiviazione disponibile sul dispositivo.

  • Chrome consente al browser di utilizzare fino all'80% dello spazio su disco totale. Un'origine può utilizzare fino al 60% dello spazio su disco totale. Puoi utilizzare l'API StorageManager per determinare la quota massima disponibile. Altri browser basati su Chromium potrebbero essere diversi.
    • In modalità di navigazione in incognito, Chrome riduce la quantità di spazio di archiviazione che un'origine può utilizzare a circa il 5% dello spazio di disco totale.
    • Se l'utente ha attivato l'opzione "Cancella cookie e dati dei siti alla chiusura di tutte le finestre" in Chrome, la quota di spazio di archiviazione viene ridotta in modo significativo a un massimo di circa 300 MB.
  • Firefox consente al browser di utilizzare fino al 50% dello spazio libero su disco. Un gruppo eTLD+1 (ad es. example.com, www.example.com e foo.bar.example.com) potrebbe utilizzare fino a 2 GB. Puoi utilizzare l'API StorageManager per determinare quanto spazio è ancora disponibile.
  • Safari (sia su computer che su dispositivi mobili) sembra consentire circa 1 GB. Quando viene raggiunto il limite, Safari chiede all'utente di aumentare il limite in incrementi di 200 MB. Non sono riuscito a trovare documentazione ufficiale in merito.
    • Se una PWA viene aggiunta alla home page su Safari mobile, viene creato un nuovo contenitore di archiviazione e non viene condiviso nulla tra la PWA e Safari mobile. Una volta raggiunta la quota per una PWA installata, sembra non esserci modo di richiedere spazio di archiviazione aggiuntivo.

In passato, se un sito superava una determinata soglia di dati memorizzati, il browser chiedeva all'utente di concedere l'autorizzazione per l'utilizzo di più dati. Ad esempio, se l'origine ha utilizzato più di 50 MB, il browser chiede all'utente di consentire lo spazio di archiviazione fino a 100 MB, quindi chiede di nuovo con incrementi di 50 MB.

Oggi, la maggior parte dei browser moderni non chiederà all'utente e consentirà a un sito di utilizzare fino alla quota assegnata. L'eccezione sembra essere Safari, che richiede l'autorizzazione per aumentare la quota allocata quando viene superata la quota di spazio di archiviazione. Se un'origine tenta di utilizzare più della quota assegnata, gli ulteriori tentativi di scrittura dei dati non andranno a buon fine.

Come faccio a controllare la quantità di spazio di archiviazione disponibile?

In molti browser, puoi utilizzare l'API StorageManager per determinare la quantità di spazio di archiviazione disponibile per l'origine e la quantità di spazio di archiviazione in uso. Indica il numero totale di byte utilizzati da IndexedDB e dall'API Cache e consente di calcolare lo spazio di archiviazione rimanente approssimativo disponibile.

if (navigator.storage && navigator.storage.estimate) {
  const quota = await navigator.storage.estimate();
  // quota.usage -> Number of bytes used.
  // quota.quota -> Maximum number of bytes available.
  const percentageUsed = (quota.usage / quota.quota) * 100;
  console.log(`You've used ${percentageUsed}% of the available storage.`);
  const remaining = quota.quota - quota.usage;
  console.log(`You can write up to ${remaining} more bytes.`);
}

Devi rilevare gli errori di superamento della quota (vedi di seguito). In alcuni casi, è possibile che la quota disponibile superi la quantità effettiva di spazio di archiviazione disponibile.

Ispeziona

Durante lo sviluppo, puoi utilizzare gli strumenti per gli sviluppatori del browser per ispezionare i diversi tipi di archiviazione ed eliminare tutti i dati memorizzati.

In Chrome 88 è stata aggiunta una nuova funzionalità che ti consente di ignorare la quota di archiviazione del sito nel riquadro Spazio di archiviazione. Questa funzionalità ti consente di simulare diversi dispositivi e di testare il comportamento delle tue app in scenari di bassa disponibilità del disco. Vai a Applicazione e poi a Spazio di archiviazione, attiva la casella di controllo Simula quota di spazio di archiviazione personalizzata e inserisci un numero valido per simulare la quota di spazio di archiviazione.

Mentre lavoravo a questa guida, ho scritto un semplice strumento per tentare di utilizzare rapidamente il maggior spazio di archiviazione possibile. È un modo rapido per sperimentare diversi meccanismi di archiviazione e vedere cosa succede quando utilizzi tutta la quota.

Come gestire il superamento della quota?

Che cosa devi fare quando superi la quota? Soprattutto, devi sempre rilevare e gestire gli errori di scrittura, che si tratti di un QuotaExceededError o di qualcos'altro. Poi, a seconda del design dell'app, decidi come gestirla. Ad esempio, elimina i contenuti a cui non è stato eseguito l'accesso da molto tempo, rimuovi i dati in base alle dimensioni o offri agli utenti un modo per scegliere cosa eliminare.

Sia IndexedDB sia l'API Cache generano un DOMError chiamato QuotaExceededError quando superi la quota disponibile.

IndexedDB

Se l'origine ha superato la quota, i tentativi di scrittura in IndexedDB non andranno a buon fine. Verrà chiamato il gestore onabort() della transazione, passando un evento. L'evento includerà un DOMException nella proprietà error. Se controlli l'errore name, viene restituito QuotaExceededError.

const transaction = idb.transaction(['entries'], 'readwrite');
transaction.onabort = function(event) {
  const error = event.target.error; // DOMException
  if (error.name == 'QuotaExceededError') {
    // Fallback code goes here
  }
};

API Cache

Se l'origine ha superato la quota, i tentativi di scrittura nell'API Cache verranno rifiutati con un QuotaExceededError DOMException.

try {
  const cache = await caches.open('my-cache');
  await cache.add(new Request('/sample1.jpg'));
} catch (err) {
  if (error.name === 'QuotaExceededError') {
    // Fallback code goes here
  }
}

Come funziona l'espulsione?

Lo spazio di archiviazione web è suddiviso in due bucket: "Best Effort" e "Permanente". Il criterio del "best effort" significa che lo spazio di archiviazione può essere cancellato dal browser senza interrompere l'utente, ma è meno duraturo per i dati critici o a lungo termine. Lo spazio di archiviazione permanente non viene cancellato automaticamente quando lo spazio è ridotto. L'utente deve cancellare manualmente questo spazio di archiviazione (tramite le impostazioni del browser).

Per impostazione predefinita, i dati di un sito (inclusi IndexedDB, API Cache e così via) rientrano nella categoria del massimo impegno, il che significa che, a meno che un sito non abbia richiesto uno spazio di archiviazione permanente, il browser può eliminare i dati del sito a sua discrezione, ad esempio quando lo spazio di archiviazione del dispositivo è basso.

Il criterio di espulsione per il criterio del massimo impegno è:

  • I browser basati su Chromium inizieranno a eliminare i dati quando lo spazio del browser sarà esaurito, cancellando prima tutti i dati del sito dell'origine utilizzata meno di recente, poi quelli della successiva, fino a quando il browser non supererà più il limite.
  • Firefox inizierà a eliminare i dati quando lo spazio su disco disponibile sarà esaurito, cancellando prima tutti i dati del sito dall'origine utilizzata meno di recente, poi dalla successiva, fino a quando il browser non supererà più il limite.
  • In precedenza Safari non espelleva i dati, ma di recente ha implementato un nuovo limite di sette giorni per tutto lo spazio di archiviazione scrivibile (vedi di seguito).

A partire da iOS e iPadOS 13.4 e Safari 13.1 su macOS, è previsto un limite di sette giorni per tutto lo spazio di archiviazione dello script scrivibile, tra cui IndexedDB, la registrazione dei worker di servizio e l'API Cache. Ciò significa che Safari rimuoverà tutti i contenuti dalla cache dopo sette giorni di utilizzo se l'utente non interagisce con il sito. Questo criterio di espulsione non si applica alle PWA installate che sono state aggiunte alla schermata Home. Per informazioni dettagliate, consulta Bloccare completamente i cookie di terze parti e altro ancora sul blog di WebKit.

Bucket di archiviazione

L'idea di base dell'API Storage Buckets è concedere ai siti la possibilità di creare più bucket di archiviazione, in cui il browser può scegliere di eliminare ogni bucket indipendentemente dagli altri. In questo modo gli sviluppatori possono specificare la priorità dell'espulsione per assicurarsi che i dati più importanti non vengano eliminati.

Bonus: perché utilizzare un wrapper per IndexedDB

IndexedDB è un'API di basso livello che richiede una configurazione significativa prima dell'uso, che può essere particolarmente problematica per l'archiviazione di dati di bassa complessità. A differenza della maggior parte delle API moderne basate su promesse, è basata su eventi. I wrapper delle promesse come idb per IndexedDB nascondono alcune delle funzionalità avanzate, ma soprattutto, nascondono la complessa strumentazione (ad es. transazioni, versionamento dello schema) in dotazione con la libreria IndexedDB.

Bonus: SQLite Wasm

Dopo che SQL web è stato ritirato e rimosso da Chrome, Google ha collaborato con i manutentori del popolare database SQLite per offrire un sostituto di SQL web basato su SQLite. Leggi SQLite Wasm nel browser supportato dal file system privato di Origin per informazioni dettagliate su come utilizzarlo.

Conclusione

Sono finiti i tempi dello spazio di archiviazione limitato e della richiesta all'utente di archiviare sempre più dati. I siti possono archiviare in modo efficace tutte le risorse e i dati di cui hanno bisogno per funzionare. Con l'API StorageManager puoi determinare quanto spazio di archiviazione è a tua disposizione e quanto ne hai utilizzato. Inoltre, con lo spazio di archiviazione permanente, a meno che l'utente non lo rimuova, puoi proteggerlo dall'espulsione.

Risorse aggiuntive

Grazie

Un ringraziamento speciale a Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink e Victor Costan per aver esaminato questa guida. Grazie a Eiji Kitamura, Addy Osmani e Marc Cohen, che hanno scritto gli articoli originali su cui si basa questo articolo. Eiji ha scritto uno strumento utile chiamato Browser Storage Abuser che è stato utile per convalidare il comportamento attuale. Ti consente di archiviare il maggior numero possibile di dati e di visualizzare i limiti di archiviazione sul browser. Grazie a François Beaufort che ha analizzato Safari per scoprire i suoi limiti di spazio di archiviazione e a Thomas Steiner per aver aggiunto informazioni sul file system privato di origine, sui bucket di archiviazione, su SQLite Wasm e su un aggiornamento complessivo dei contenuti nel 2024.

L'immagine hero è di Guillaume Bolduc su Unsplash.