Speicherplatz für das Web

Es gibt viele verschiedene Möglichkeiten, Daten im Browser zu speichern. Welche Option ist für Sie am besten geeignet?

Unterwegs kann es zu instabilen oder nicht vorhandenen Internetverbindungen kommen. Daher sind Offlineunterstützung und zuverlässige Leistung häufige Funktionen in progressiven Web-Apps. Auch in Umgebungen mit perfekter WLAN-Verbindung kann die Nutzerfreundlichkeit durch den umsichtigen Einsatz von Caching und anderen Speichertechniken erheblich verbessert werden. Es gibt verschiedene Möglichkeiten, statische Anwendungsressourcen (HTML, JavaScript, CSS, Bilder usw.) und Daten (Nutzerdaten, Nachrichtenartikel usw.) zu cachen. Aber welche Lösung ist die beste? Wie viel Speicherplatz steht zur Verfügung? Wie kann ich verhindern, dass sie entfernt wird?

Was soll ich verwenden?

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 aber auch eine synchrone Variante des OPFS, die ausschließlich in Webworkern verfügbar ist. Sie sind über das window-Objekt, Webworker und Service Worker zugänglich, sodass sie überall in Ihrem Code verwendet werden können.

Was ist mit anderen Speichermechanismen?

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

SessionStorage ist tabspezifisch und auf die Lebensdauer des Tabs beschränkt. Er kann nützlich sein, um kleine Mengen sitzungsspezifischer 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 darf nur Strings enthalten. Da sie tabspezifisch ist, kann nicht über Web- oder Service-Worker darauf zugegriffen werden.

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

Cookies haben ihre Berechtigung, 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, wird die Größe jeder Webanfrage erheblich erhöht. Sie sind synchron und können nicht über Web-Worker aufgerufen werden. Wie LocalStorage und SessionStorage sind Cookies auf Strings beschränkt.

Die File System Access API wurde entwickelt, um Nutzern das Lesen und Bearbeiten von Dateien in ihrem lokalen Dateisystem zu ermöglichen. Der Nutzer muss die Berechtigung erteilen, bevor eine Seite in eine lokale Datei lesen oder schreiben kann. Berechtigungen werden nicht sitzungsübergreifend beibehalten, es sei denn, ein Dateihandle wird in IndexedDB zwischengespeichert. Die File System Access API eignet sich am besten für Anwendungsfälle wie Editoren, in denen Sie eine Datei öffnen, ändern und die Änderungen dann möglicherweise 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. Sie ist zwar asynchron, wird aber nicht empfohlen, da sie nur in Chromium-basierten Browsern verfügbar ist.

Wie viel Speicherplatz habe ich?

Kurz gesagt: Viel. Mindestens einige Hundert Megabyte und möglicherweise Hunderte von Gigabyte oder mehr. Browserimplementierungen variieren, aber die Menge des verfügbaren Speichers basiert in der Regel auf der Menge des auf dem Gerät verfügbaren Speichers.

  • Chrome darf bis zu 80% des gesamten Speicherplatzes verwenden. Ein Ursprung kann bis zu 60% des gesamten Speicherplatzes belegen. Mit der StorageManager API können Sie das maximal verfügbare Kontingent ermitteln. Bei anderen Chromium-basierten Browsern kann das 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 Speicherplatzkontingent erheblich auf maximal etwa 300 MB reduziert.
  • Firefox darf bis zu 50% des freien Speicherplatzes verwenden. Eine Gruppe mit eTLD+1 (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.
  • In Safari (sowohl auf Computern als auch auf Mobilgeräten) sind etwa 1 GB möglich. Wenn das Limit erreicht ist, werden Nutzer in Safari aufgefordert, das Limit in Schritten von 200 MB zu erhöhen. Ich konnte dazu keine offizielle Dokumentation finden.
    • Wenn eine PWA in Mobile Safari dem Startbildschirm hinzugefügt wird, wird ein neuer Speichercontainer erstellt. Es werden keine Daten zwischen der PWA und Mobile Safari geteilt. Wenn 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 Schwellenwert für gespeicherte Daten überschritt, forderte der Browser den Nutzer auf, die Berechtigung zur Verwendung weiterer Daten zu erteilen. Wenn der Ursprung beispielsweise mehr als 50 MB verwendet hat, fordert der Browser den Nutzer auf, bis zu 100 MB zu speichern, und fragt dann in Schritten von 50 MB noch einmal nach.

In den meisten modernen Browsern wird der Nutzer heute nicht mehr aufgefordert, die Nutzung des Speichers zu bestätigen. Stattdessen kann eine Website bis zu ihrem zugewiesenen Kontingent verwendet werden. Die Ausnahme scheint Safari zu sein. Dort wird eine Aufforderung angezeigt, wenn das Speicherkontingent überschritten wird, in der um die Erlaubnis gebeten wird, das zugewiesene Kontingent zu erhöhen. Wenn ein Ursprung versucht, mehr als sein zugewiesenes Kontingent zu verwenden, schlagen weitere Versuche, Daten zu schreiben, fehl.

Wie kann ich prüfen, wie viel Speicherplatz 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 verwendet. Sie gibt die Gesamtzahl der von IndexedDB und der Cache API verwendeten Byte an und ermöglicht es, den ungefähren verbleibenden Speicherplatz zu berechnen.

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 Überschreitungen 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 DevTools Ihres Browsers verwenden, um die verschiedenen Speichertypen zu untersuchen und alle gespeicherten Daten zu löschen.

In Chrome 88 wurde eine neue Funktion hinzugefügt, mit der Sie das Speicherplatzkontingent der Website im Bereich „Speicher“ überschreiben können. Mit dieser Funktion können Sie verschiedene Geräte simulieren und das Verhalten Ihrer Apps bei geringem Speicherplatz testen. Gehen Sie zu Application (Anwendung) und dann zu Storage (Speicher). Aktivieren Sie das Kästchen Simulate custom storage quota (Benutzerdefiniertes Speicherkontingent simulieren) und geben Sie eine beliebige gültige Zahl ein, um das Speicherkontingent zu simulieren.

Während ich an diesem Leitfaden gearbeitet habe, habe ich ein einfaches Tool geschrieben, um möglichst schnell so viel Speicherplatz wie möglich zu belegen. So können Sie schnell verschiedene Speichermechanismen ausprobieren und sehen, was passiert, wenn Sie Ihr gesamtes Kontingent nutzen.

Was kann ich tun, 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-Fehler 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 lange nicht zugegriffen wurde, Daten anhand der Größe entfernen oder Nutzern die Möglichkeit geben, selbst zu entscheiden, was sie löschen möchten.

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

IndexedDB

Wenn der Ursprung sein Kontingent überschritten hat, schlagen Versuche, in IndexedDB zu schreiben, fehl. Der onabort()-Handler der Transaktion wird aufgerufen und ein Ereignis wird übergeben. Das Ereignis enthält ein DOMException im Fehlerattribut. 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 Herkunftsland sein Kontingent überschritten hat, werden Versuche, in die Cache API zu schreiben, 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 Kündigung?

Webspeicher wird in zwei Kategorien unterteilt: „Best Effort“ und „Persistent“. „Best Effort“ bedeutet, dass der Speicher vom Browser geleert werden kann, ohne den Nutzer zu unterbrechen. Diese Methode ist jedoch weniger zuverlässig für langfristige oder kritische Daten. Der persistente Speicher wird nicht automatisch geleert, wenn der Speicherplatz knapp wird. Der Nutzer muss diesen Speicher 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 Websitedaten nach eigenem Ermessen löschen kann, z. B. wenn der Gerätespeicherplatz knapp ist, es sei denn, eine Website hat persistenten Speicher angefordert.

Die Richtlinie zum Entfernen von Best-Effort-Instanzen lautet:

  • Chromium-basierte Browser beginnen mit dem Entfernen von Daten, wenn der Speicherplatz des Browsers erschöpft ist. Dabei werden zuerst alle Websitedaten des am wenigsten verwendeten Ursprungs gelöscht, dann die des nächsten usw., bis der Browser das Limit nicht mehr überschreitet.
  • Firefox beginnt mit dem Entfernen von Daten, wenn der verfügbare Speicherplatz voll ist. Dabei werden zuerst alle Websitedaten des am längsten nicht verwendeten Ursprungs gelöscht, dann die des nächsten usw., bis der Browser das Limit nicht mehr überschreitet.
  • Bisher wurden in Safari keine Daten entfernt, aber vor Kurzem wurde eine neue Obergrenze von sieben Tagen für alle beschreibbaren Speicher implementiert (siehe unten).

Ab iOS und iPadOS 13.4 sowie Safari 13.1 unter macOS gilt eine Beschränkung von sieben Tagen für alle von Skripts beschreibbaren Speicher, einschließlich IndexedDB, Service Worker-Registrierung und Cache API. Das bedeutet, dass Safari nach sieben Tagen Nutzung alle Inhalte aus dem Cache entfernt, wenn der Nutzer nicht mit der Website interagiert. Diese Richtlinie zum Entfernen von Apps 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.

Storage-Buckets

Die Storage Buckets API ermöglicht es Websites, mehrere Storage-Buckets zu erstellen, die der Browser unabhängig voneinander löschen kann. So können Entwickler die Priorisierung für das Entfernen von Daten festlegen, um sicherzustellen, dass die wertvollsten Daten nicht gelöscht werden.

Bonus: Warum sollte man einen Wrapper für IndexedDB verwenden?

IndexedDB ist eine Low-Level-API, die vor der Verwendung eine umfangreiche Einrichtung erfordert. Das kann insbesondere bei der Speicherung von Daten mit geringer Komplexität problematisch sein. Im Gegensatz zu den meisten modernen Promise-basierten APIs ist sie ereignisbasiert. Promise-Wrapper wie idb für IndexedDB blenden einige der leistungsstarken Funktionen aus, aber vor allem die komplexe Maschinerie (z.B. Transaktionen, Schemaversionierung), die mit der IndexedDB-Bibliothek einhergeht.

Bonus: SQLite Wasm

Nachdem Web SQL eingestellt und aus Chrome entfernt wurde, hat Google mit den Maintainern der beliebten SQLite-Datenbank zusammengearbeitet, um einen auf SQLite basierenden Ersatz für Web SQL anzubieten. Weitere Informationen zur Verwendung finden Sie unter SQLite Wasm in the browser backed by the Origin Private File System.

Fazit

Die Zeiten, in denen der Speicherplatz begrenzt war und Nutzer aufgefordert wurden, immer mehr Daten zu speichern, sind vorbei. Websites können praktisch alle Ressourcen und Daten speichern, die für die Ausführung erforderlich sind. Mit der StorageManager API können Sie ermitteln, wie viel Speicherplatz Ihnen zur Verfügung steht und wie viel Sie bereits genutzt haben. Mit persistent storage (dauerhafter Speicher) können Sie Daten vor dem Entfernen schützen, sofern der Nutzer sie nicht entfernt.

Zusätzliche Ressourcen

Vielen Dank

Besonderer Dank gilt Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink und Victor Costan für das Überprüfen dieses Leitfadens. Vielen Dank an Eiji Kitamura, Addy Osmani und Marc Cohen, die die Originalartikel geschrieben haben, auf denen dieser Artikel basiert. Eiji hat ein hilfreiches Tool namens Browser Storage Abuser entwickelt, mit dem sich das aktuelle Verhalten validieren lässt. So können Sie so viele Daten wie möglich speichern und die Speicherlimits in Ihrem Browser einsehen. Vielen Dank an François Beaufort, der sich mit Safari beschäftigt hat, um die Speicherlimits herauszufinden, und an Thomas Steiner, der 2024 Informationen zum privaten Dateisystem des Ursprungs, zu Speicher-Buckets und zu SQLite Wasm hinzugefügt und die Inhalte insgesamt aktualisiert hat.

Das Hero-Image stammt von Guillaume Bolduc auf Unsplash.