Speicherplatz für das Web

Es gibt viele verschiedene Möglichkeiten, Daten im Browser zu speichern. Welches passt am besten zu Ihren Anforderungen?

Internetverbindungen können unterwegs unzuverlässig sein oder gar nicht vorhanden sein. Daher sind Offlineunterstützung und zuverlässige Leistung gängige Funktionen in progressiven Web-Apps. Selbst in perfekten drahtlosen Umgebungen kann die Nutzerfreundlichkeit durch eine sinnvolle Verwendung von Caching und anderen Speichertechniken erheblich verbessert werden. Es gibt mehrere Möglichkeiten, Ihre statischen Anwendungsressourcen (HTML, JavaScript, CSS, Bilder usw.) und Daten (Nutzerdaten, Nachrichtenartikel usw.) im Cache zu speichern. Aber welche Lösung ist die beste? Wie viel kann ich speichern? Wie kann ich verhindern, dass es entfernt wird?

Hier ist eine allgemeine Empfehlung zum Speichern von Ressourcen:

IndexedDB, OPFS und die Cache Storage API werden von allen modernen Browsern unterstützt. Sie sind asynchron und blockieren den Hauptthread nicht. Es gibt jedoch auch eine synchrone Variante der OPFS, die ausschließlich in Webworkern verfügbar ist. Sie sind über das window-Objekt, Webworker und Dienstworker zugänglich, sodass sie überall in Ihrem Code verwendet werden können.

Wie sieht es mit anderen Speichermechanismen aus?

Es gibt mehrere andere Speichermechanismen im Browser, die jedoch nur eingeschränkt nutzbar sind und erhebliche Leistungsprobleme verursachen können.

SessionStorage ist tabspezifisch und gilt nur für die Lebensdauer des Tabs. Sie kann nützlich sein, um kleine Mengen an sitzungsspezifischen Informationen zu speichern, z. B. einen IndexedDB-Schlüssel. Sie sollte mit Vorsicht verwendet werden, da sie synchron ist und den Hauptthread blockiert. Sie ist auf etwa 5 MB begrenzt und kann nur Strings enthalten. Da sie tabspezifisch ist, kann sie nicht von Webworkern oder Serviceworkern aufgerufen werden.

LocalStorage sollte vermieden werden, da es synchron ist und den Hauptthread blockiert. Sie ist auf etwa 5 MB begrenzt und kann nur Strings enthalten. Auf LocalStorage kann nicht über Webworker oder Dienstworker zugegriffen werden.

Cookies haben ihre Verwendung, sollten aber nicht zum Speichern verwendet werden. Cookies werden mit jeder HTTP-Anfrage gesendet. Wenn Sie also mehr als eine kleine Menge an Daten speichern, erhöht sich die Größe jeder Webanfrage erheblich. Sie sind synchron und können nicht von Webworkern aufgerufen werden. Wie LocalStorage und SessionStorage sind auch Cookies auf Strings beschränkt.

Die File System Access API wurde entwickelt, damit Nutzer Dateien in ihrem lokalen Dateisystem lesen und bearbeiten können. Der Nutzer muss die Berechtigung gewähren, bevor eine Seite in eine lokale Datei lesen oder in diese schreiben kann. Berechtigungen werden nicht über Sitzungen hinweg beibehalten, es sei denn, ein Dateihandle wird in IndexedDB im Cache gespeichert. Die File System Access API eignet sich am besten für Anwendungsfälle wie Editoren, bei denen Sie eine Datei öffnen, ändern und dann möglicherweise die Änderungen in der Datei speichern müssen.

Die File System API und die FileWriter API bieten Methoden zum Lesen und Schreiben von Dateien in einem Sandbox-Dateisystem. Diese Methode ist zwar asynchron, wird aber nicht empfohlen, da sie nur in Chromium-basierten Browsern verfügbar ist.

Wie viel kann ich speichern?

Kurz gesagt: Sehr viel, mindestens ein paar hundert Megabyte und potenziell Hunderte von Gigabyte oder mehr. Browserimplementierungen variieren, aber die verfügbare Speichermenge basiert in der Regel auf der auf dem Gerät verfügbaren Speichermenge.

  • Chrome erlaubt es dem Browser, bis zu 80% des gesamten Speicherplatzes zu nutzen. Ein Ursprung kann bis zu 60% des gesamten Speicherplatzes belegen. Mit der StorageManager API können Sie das verfügbare maximale Kontingent ermitteln. Bei anderen Chromium-basierten Browsern kann dies anders sein.
    • Im Inkognitomodus reduziert Chrome die Menge an Speicherplatz, die ein Ursprung verwenden kann, auf etwa 5% des gesamten Festplattenspeichers.
    • Wenn der Nutzer in Chrome die Option „Cookies und Websitedaten löschen, wenn alle Fenster geschlossen werden“ aktiviert hat, wird das Speicherkontingent deutlich auf maximal etwa 300 MB reduziert.
  • Firefox erlaubt es dem Browser, bis zu 50% des freien Speicherplatzes zu nutzen. Eine eTLD+1-Gruppe (z.B. example.com, www.example.com und foo.bar.example.com) können bis zu 2 GB verbrauchen. Mit der StorageManager API können Sie ermitteln, wie viel Speicherplatz noch verfügbar ist.
  • Safari (sowohl auf dem Computer als auch auf Mobilgeräten) erlaubt anscheinend etwa 1 GB. Wenn das Limit erreicht ist, wird der Nutzer von Safari aufgefordert, das Limit in Schritten von 200 MB zu erhöhen. Ich konnte dazu keine offiziellen Dokumente finden.
    • Wenn eine PWA dem Startbildschirm in Safari auf Mobilgeräten hinzugefügt wird, wird ein neuer Speichercontainer erstellt und es wird nichts zwischen der PWA und Safari auf Mobilgeräten geteilt. Sobald das Kontingent für eine installierte PWA erreicht ist, gibt es anscheinend keine Möglichkeit, zusätzlichen Speicherplatz anzufordern.

Wenn eine Website in der Vergangenheit einen bestimmten Grenzwert für die gespeicherten Daten überschritten hat, wurde der Nutzer vom Browser aufgefordert, die Berechtigung zur Verwendung weiterer Daten zu erteilen. Wenn der Ursprung beispielsweise mehr als 50 MB belegt, wird der Nutzer vom Browser aufgefordert, ihm zu erlauben, bis zu 100 MB zu speichern. Anschließend wird er in 50-MB-Schritten noch einmal gefragt.

Heutzutage werden Nutzer in den meisten modernen Browsern nicht mehr dazu aufgefordert und Websites können ihr zugewiesenes Kontingent voll ausschöpfen. Die Ausnahme ist offenbar Safari, das beim Überschreiten des Speicherkontingents um Erlaubnis bittet, das zugewiesene Kontingent zu erhöhen. Wenn ein Ursprung versucht, mehr als sein zugewiesenes Kontingent zu verwenden, schlagen weitere Versuche zum Schreiben von Daten fehl.

Wie kann ich prüfen, wie viel Speicherplatz noch verfügbar ist?

In vielen Browsern können Sie mit der StorageManager API ermitteln, wie viel Speicherplatz für den Ursprung verfügbar ist und wie viel Speicherplatz er belegt. Sie gibt die Gesamtzahl der von IndexedDB und der Cache API verwendeten Bytes an und ermöglicht die Berechnung des ungefähr verbleibenden verfügbaren Speicherplatzes.

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

Sie müssen Fehler aufgrund von Überschreitung des Kontingents abfangen (siehe unten). In einigen Fällen kann das verfügbare Kontingent den tatsächlich verfügbaren Speicherplatz überschreiten.

Prüfen

Während der Entwicklung können Sie die verschiedenen Speichertypen mit den DevTools Ihres Browsers prüfen und alle gespeicherten Daten löschen.

In Chrome 88 wurde eine neue Funktion hinzugefügt, mit der Sie das Speicherkontingent der Website im Bereich „Speicher“ überschreiben können. Mit dieser Funktion können Sie verschiedene Geräte simulieren und das Verhalten Ihrer Apps in Szenarien mit geringer Speicherplatzverfügbarkeit testen. Klicken Sie auf Anwendung und dann auf Speicher. Aktivieren Sie das Kästchen Benutzerdefiniertes Speicherkontingent simulieren und geben Sie eine gültige Zahl ein, um das Speicherkontingent zu simulieren.

Während der Arbeit an diesem Leitfaden habe ich ein einfaches Tool geschrieben, mit dem ich schnell so viel Speicherplatz wie möglich nutzen konnte. So können Sie schnell verschiedene Speichermechanismen ausprobieren und sehen, was passiert, wenn Sie Ihr gesamtes Kontingent aufbrauchen.

Was passiert, wenn ich mein Kontingent überschreite?

Was sollten Sie tun, wenn Sie Ihr Kontingent überschreiten? Am wichtigsten ist, dass Sie Schreibfehler immer abfangen und behandeln, unabhängig davon, ob es sich um einen QuotaExceededError oder einen anderen Fehler handelt. Entscheiden Sie dann je nach App-Design, wie Sie damit umgehen möchten. Sie können beispielsweise Inhalte löschen, auf die seit langem nicht zugegriffen wurde, Daten basierend auf der Größe entfernen oder Nutzern die Möglichkeit geben, auszuwählen, was gelöscht werden soll.

Sowohl IndexedDB als auch die Cache API werfen eine DOMError mit dem Namen QuotaExceededError, wenn Sie das verfügbare Kontingent überschritten haben.

IndexedDB

Wenn das Kontingent des Ursprungs überschritten wurde, schlagen Schreibversuche in IndexedDB fehl. Der onabort()-Handler der Transaktion wird aufgerufen und ein Ereignis übergeben. Das Ereignis enthält in der Fehlereigenschaft einen DOMException. Wenn Sie den Fehler name prüfen, wird QuotaExceededError zurückgegeben.

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

Cache API

Wenn das Kontingent des Ursprungs überschritten wurde, werden Schreibversuche in der Cache API mit einem QuotaExceededError DOMException abgelehnt.

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

Wie funktioniert die Deaktivierung?

Der Webspeicher wird in zwei Kategorien unterteilt: „Best Effort“ und „Persistent“. „Best Effort“ bedeutet, dass der Speicherplatz vom Browser gelöscht werden kann, ohne den Nutzer zu unterbrechen. Er ist jedoch weniger geeignet für langfristige oder kritische Daten. Der nichtflüchtige Speicher wird nicht automatisch gelöscht, wenn der Speicherplatz knapp wird. Der Nutzer muss diesen Speicherplatz manuell über die Browsereinstellungen leeren.

Standardmäßig fallen die Daten einer Website (einschließlich IndexedDB, Cache API usw.) in die Kategorie „Best Effort“. Das bedeutet, dass der Browser die Websitedaten nach eigenem Ermessen entfernen kann, wenn eine Website keinen persistenten Speicher angefordert hat, z. B. wenn der Gerätespeicher niedrig ist.

Die Richtlinie für die Auslagerung nach dem Best-Effort-Prinzip lautet:

  • In Chromium-basierten Browsern werden Daten entfernt, wenn der Speicherplatz des Browsers aufgebraucht ist. Dabei werden zuerst alle Websitedaten des am wenigsten verwendeten Ursprungs gelöscht, dann die des nächsten, bis das Limit nicht mehr überschritten wird.
  • Wenn der verfügbare Speicherplatz belegt ist, beginnt Firefox, Daten zu entfernen. Dabei werden zuerst alle Websitedaten aus der am längsten nicht verwendeten Quelle und dann aus der nächsten gelöscht, bis das Limit nicht mehr überschritten wird.
  • Bisher wurden in Safari keine Daten entfernt. Vor Kurzem wurde jedoch eine neue Obergrenze von sieben Tagen für den gesamten beschreibbaren Speicherplatz eingeführt (siehe unten).

Ab iOS und iPadOS 13.4 und Safari 13.1 auf macOS gilt für alle Scripts, die Speicherplatz belegen können, eine Speicherdauer von sieben Tagen. Dazu gehören IndexedDB, die Registrierung von Dienstarbeitern und die Cache API. Das bedeutet, dass Safari nach sieben Tagen Nutzung von Safari alle Inhalte aus dem Cache entfernt, wenn der Nutzer nicht mit der Website interagiert. Diese Richtlinie gilt nicht für installierte PWAs, die dem Startbildschirm hinzugefügt wurden. Ausführliche Informationen finden Sie im WebKit-Blog unter Full Third-Party Cookie Blocking and More (Vollständiges Blockieren von Drittanbieter-Cookies und mehr).

Storage-Buckets

Der Hauptzweck der Storage Buckets API besteht darin, Websites die Möglichkeit zu geben, mehrere Storage Buckets zu erstellen, wobei der Browser jeden Bucket unabhängig von anderen löschen kann. So können Entwickler die Auslagerung priorisieren, damit die wertvollsten Daten nicht gelöscht werden.

Bonus: Warum einen Wrapper für IndexedDB verwenden

IndexedDB ist eine Low-Level-API, die vor der Verwendung umfangreiche Einrichtungsschritte erfordert. Das kann besonders mühsam sein, wenn Sie Daten mit geringer Komplexität speichern möchten. Im Gegensatz zu den meisten modernen versprechenbasierten APIs ist sie ereignisbasiert. Promise-Wrapper wie idb für IndexedDB verbergen einige der leistungsstarken Funktionen, aber vor allem die komplexen Abläufe (z.B. Transaktionen, Schemaversionierung), die mit der IndexedDB-Bibliothek verbunden sind.

Bonus: SQLite Wasm

Nachdem Web SQL eingestellt und aus Chrome entfernt wurde, hat Google mit den Entwicklern der beliebten SQLite-Datenbank zusammengearbeitet, um einen Web SQL-Ersatz auf SQLite-Basis anzubieten. Weitere Informationen zur Verwendung finden Sie unter SQLite Wasm im Browser, unterstützt vom Origin Private File System.

Fazit

Vorbei sind die Zeiten, in denen der Speicherplatz begrenzt war und Nutzer immer mehr Daten speichern mussten. Websites können alle erforderlichen Ressourcen und Daten effizient speichern. Mit der StorageManager API können Sie ermitteln, wie viel Speicherplatz Ihnen zur Verfügung steht und wie viel Sie bereits belegt haben. Und mit persistentem Speicher können Sie die Daten vor dem Löschen schützen, es sei denn, der Nutzer entfernt sie.

Zusätzliche Ressourcen

Vielen Dank

Besonderer Dank geht an Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink und Victor Costan, die diesen Leitfaden geprüft haben. Vielen Dank an Eiji Kitamura, Addy Osmani und Marc Cohen, die die ursprünglichen Artikel verfasst haben, auf denen dieser Artikel basiert. Eiji hat ein hilfreiches Tool namens Browser Storage Abuser entwickelt, mit dem sich das aktuelle Verhalten überprüfen lässt. So können Sie so viele Daten wie möglich speichern und die Speicherlimits in Ihrem Browser sehen. Vielen Dank an François Beaufort, der die Speicherlimits von Safari ermittelt hat, und an Thomas Steiner, der Informationen zum ursprünglichen privaten Dateisystem, zu Speicher-Buckets, zu SQLite Wasm und zu einer allgemeinen Inhaltsaktualisierung im Jahr 2024 hinzugefügt hat.

Das Hero-Image stammt von Guillaume Bolduc bei Unsplash.