Speicherplatz für das Web

Es gibt viele verschiedene Möglichkeiten, Daten im Browser zu speichern. Welche ist die beste für Ihre Anforderungen?

Internetverbindungen können unterwegs unzuverlässig oder nicht vorhanden sein. Deshalb sind Offline-Support und zuverlässige Leistung häufige Funktionen in progressiven Web-Apps. Auch in perfekten WLAN-Umgebungen kann die umsichtige Verwendung von Caching und anderen Speichertechniken die Nutzerfreundlichkeit erheblich verbessern. Es gibt verschiedene 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 können Sie speichern? Wie verhindern Sie, dass die Daten entfernt werden?

Was sollte ich verwenden?

Hier ist eine allgemeine Empfehlung zum Speichern von Ressourcen:

IndexedDB, das OPFS und die Cache Storage API werden in 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 Web Workern verfügbar ist. Sie sind über das window-Objekt, Web Worker 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, aber ihre Verwendung ist begrenzt und sie können erhebliche Leistungsprobleme verursachen.

SessionStorage ist tabspezifisch und auf die Lebensdauer des Tabs beschränkt. Es kann nützlich sein, um kleine Mengen sitzungsspezifischer Informationen zu speichern, z. B. einen IndexedDB-Schlüssel. Es sollte mit Vorsicht verwendet werden, da es synchron ist und den Hauptthread blockiert. Es ist auf etwa 5 MB begrenzt und kann nur Strings enthalten. Da es tabspezifisch ist, ist es nicht über Web Worker oder Service Worker zugänglich.

LocalStorage sollte vermieden werden, da es synchron ist und den Hauptthread blockiert. Es ist auf etwa 5 MB begrenzt und kann nur Strings enthalten. LocalStorage ist nicht über Web Worker oder Service Worker zugänglich.

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, wird die Größe jeder Webanfrage erheblich erhöht. Sie sind synchron und nicht über Web Worker zugänglich. 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 auf ihrem lokalen Dateisystem zu ermöglichen. Der Nutzer muss die Berechtigung erteilen, bevor eine Seite eine lokale Datei lesen oder in sie schreiben kann. Berechtigungen werden nicht sitzungsübergreifend 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, bearbeiten und dann die Änderungen möglicherweise wieder in der Datei speichern müssen.

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

Wie viel kann ich speichern?

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

  • In Chrome kann der Browser bis zu 80% des gesamten Festplattenspeichers verwenden. Ein Ursprung kann bis zu 60% des gesamten Festplattenspeichers verwenden. 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 des Speichers, 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 erheblich auf maximal etwa 300 MB reduziert.
  • In Firefox kann der Browser bis zu 50% des kostenlosen Festplattenspeichers verwenden. Eine eTLD+1 Gruppe (z.B. example.com, www.example.com und foo.bar.example.com) kann bis zu 2 GB verwenden. Mit der StorageManager API können Sie ermitteln, wie viel Speicherplatz noch verfügbar ist.
  • Safari (sowohl auf Computern als auch auf Mobilgeräten) scheint etwa 1 GB zu ermöglichen. Wenn das Limit erreicht ist, fordert Safari den Nutzer auf, das Limit in Schritten von 200 MB zu erhöhen. Ich konnte keine offizielle Dokumentation dazu finden.
    • Wenn eine PWA auf dem Startbildschirm von Mobile Safari hinzugefügt wird, wird ein neuer Speichercontainer erstellt. Es werden keine Daten zwischen der PWA und Mobile Safari geteilt. Sobald das Kontingent für eine installierte PWA erreicht ist, scheint es keine Möglichkeit zu geben, zusätzlichen Speicher anzufordern.

Wenn eine Website in der Vergangenheit einen bestimmten Grenzwert für gespeicherte Daten überschritten hat, hat der Browser den Nutzer aufgefordert, die Berechtigung zur Verwendung weiterer Daten zu erteilen. Wenn der Ursprung beispielsweise mehr als 50 MB verwendet hat, hat der Browser den Nutzer aufgefordert, bis zu 100 MB zu speichern, und dann in Schritten von 50 MB noch einmal gefragt.

Heute fordern die meisten modernen Browser den Nutzer nicht auf und erlauben einer Website, bis zu ihrem zugewiesenen Kontingent zu verwenden. Die Ausnahme scheint Safari zu sein, das eine Aufforderung anzeigt, wenn das Speicherkontingent überschritten wird, und um die Berechtigung bittet, 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 die Menge des Speichers ermitteln, der dem Ursprung zur Verfügung steht, und wie viel Speicher er verwendet. Sie meldet die Gesamtzahl der von IndexedDB und der Cache API verwendeten Byte und ermöglicht die Berechnung des ungefähren verbleibenden 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 Kontingentüberschreitungen abfangen (siehe unten). In einigen Fällen kann das verfügbare Kontingent die tatsächlich verfügbare Speichermenge überschreiten.

Prüfen

Während der Entwicklung können Sie mit den Entwicklertools Ihres Browsers die verschiedenen Speichertypen 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 Festplattenverfügbarkeit testen. Gehen Sie zu Anwendung > Speicher, aktivieren Sie das Benutzerdefiniertes Speicherkontingent simulieren Kästchen, und geben Sie eine beliebige gültige Zahl ein, um das Speicherkontingent zu simulieren.

Während der Arbeit an dieser Anleitung habe ich ein einfaches Tool geschrieben, um zu versuchen, so viel Speicher wie möglich zu verwenden. So können Sie schnell mit verschiedenen Speichermechanismen experimentieren und sehen, was passiert, wenn Sie Ihr gesamtes Kontingent verwenden.

Wie gehe ich mit Kontingentüberschreitungen um?

Was sollten Sie tun, wenn Sie das Kontingent überschreiten? Am wichtigsten ist, dass Sie immer Schreibfehler abfangen und behandeln, egal ob es sich um einen QuotaExceededError oder etwas anderes handelt. Entscheiden Sie dann je nach App-Design, wie Sie damit umgehen. Sie können beispielsweise Inhalte löschen, auf die seit längerer Zeit nicht mehr zugegriffen wurde, Daten basierend auf der Größe entfernen oder Nutzern die Möglichkeit geben, auszuwählen, was sie löschen möchten.

Sowohl IndexedDB als auch die Cache API geben einen 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 übergeben. Das Ereignis enthält eine DOMException in der Fehlerproperty. Wenn Sie den name des Fehlers 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 der Ursprung sein Kontingent überschritten hat, schlagen Versuche, in die Cache API zu schreiben, mit einer QuotaExceededError DOMException fehl.

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 das Entfernen von Daten?

Webspeicher wird in zwei Buckets kategorisiert: „Best Effort“ und „Persistent“. „Best Effort“ bedeutet, dass der Speicher vom Browser gelöscht werden kann, ohne den Nutzer zu unterbrechen. Er ist aber weniger dauerhaft für langfristige oder kritische Daten. Nichtflüchtiger Speicher wird nicht automatisch gelöscht, wenn der Speicherplatz knapp wird. Der Nutzer muss diesen Speicher manuell löschen (über die Browsereinstellungen).

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 entfernen kann, es sei denn, eine Website hat persistenten Speicher angefordert, z. B. wenn der Gerätespeicher knapp wird.

Die Richtlinie zum Entfernen von Daten für „Best Effort“ lautet:

  • Chromium-basierte Browser beginnen mit dem Entfernen von Daten, wenn der Speicherplatz des Browsers knapp wird. Dabei werden zuerst alle Websitedaten des am wenigsten verwendeten Ursprungs gelöscht, dann die des nächsten, bis der Browser das Limit nicht mehr überschreitet.
  • Firefox beginnt mit dem Entfernen von Daten, wenn der verfügbare Festplattenspeicher voll ist. Dabei werden zuerst alle Websitedaten des am wenigsten verwendeten Ursprungs gelöscht, dann die des nächsten, bis der Browser das Limit nicht mehr überschreitet.
  • Safari hat bisher keine Daten entfernt, aber vor Kurzem eine neue Obergrenze von sieben Tagen für alle beschreibbaren Speicher eingeführt (siehe unten).

Ab iOS und iPadOS 13.4 und Safari 13.1 unter macOS gilt eine Obergrenze von sieben Tagen für alle beschreibbaren Speicher für Skripts, einschließlich IndexedDB, Service Worker-Registrierung und Cache API. Das bedeutet, dass Safari alle Inhalte nach sieben Tagen Nutzung aus dem Cache entfernt, wenn der Nutzer nicht mit der Website interagiert. Diese Richtlinie zum Entfernen von Daten gilt nicht für installierte PWAs , die dem Startbildschirm hinzugefügt wurden. Vollständige Informationen finden Sie im WebKit Blog unter Full Third-Party Cookie Blocking and More.

Storage-Buckets

Die Kernidee der Storage Buckets API ist es, Websites die Möglichkeit zu geben, mehrere Storage-Buckets zu erstellen, wobei der Browser jeden Bucket unabhängig von anderen Buckets löschen kann. So können Entwickler die Priorisierung beim Entfernen von Daten festlegen, um zu verhindern, dass die wertvollsten Daten gelöscht werden.

Bonus: Warum einen Wrapper für IndexedDB verwenden?

IndexedDB ist eine API auf niedriger Ebene, die vor der Verwendung eine erhebliche Einrichtung erfordert. Das kann besonders mühsam sein, wenn Daten mit geringer Komplexität gespeichert werden sollen. Im Gegensatz zu den meisten modernen Promises-basierten APIs ist sie ereignisbasiert. Promise-Wrapper wie idb für IndexedDB blenden einige der leistungsstarken Funktionen aus, aber mehr wichtig, blenden die komplexe Maschinerie (z.B. Transaktionen, Schemaversionierung) aus, die mit der IndexedDB-Bibliothek einhergeht.

Bonus: SQLite Wasm

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

Fazit

Die Zeiten von begrenztem Speicherplatz und der Aufforderung an den Nutzer, 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 verwendet haben. Und mit nichtflüchtigem Speicher können Sie ihn vor dem Entfernen schützen, es sei denn, der Nutzer entfernt ihn.

Zusätzliche Ressourcen

Vielen Dank

Vielen Dank an Jarryd Goodman, Phil Walton, Eiji Kitamura, Daniel Murphy, Darwin Huang, Josh Bell, Marijn Kruisselbrink und Victor Costan für die Überprüfung dieser Anleitung. Vielen Dank an Eiji Kitamura, Addy Osmani und Marc Cohen, die die Originalartikel verfasst haben, auf denen diese Anleitung basiert. Eiji hat ein hilfreiches Tool namens Browser Storage Abuser geschrieben, das bei der Validierung des aktuellen Verhaltens nützlich war. Damit können Sie so viele Daten wie möglich speichern und die Speicherlimits in Ihrem Browser sehen. Vielen Dank an François Beaufort, der Safari untersucht hat, um die Speicherlimits zu ermitteln, und an Thomas Steiner, der 2024 Informationen zum Origin Private Dateisystem, zu Storage-Buckets und zu SQLite Wasm hinzugefügt und die Inhalte insgesamt aktualisiert hat.