Zum Inhalt springen
HexaTransfer
Zurück zum Blog
Technische Vertiefungen

Browser Speicher APIs für Dateiübertragung Applications

Leverage IndexedDB, File System Access API, und Cache API für Datei transfer apps. Speicher limits, performance, und browser compatibility.

Wer unter DSGVO und BDSG personenbezogene Daten verarbeitet, muss die technischen Maßnahmen zum Datenschutz dokumentieren — und der Browser-Speicher ist dabei eine der am häufigsten übersehenen Schichten. Browser-Speicher für eine Dateiübertragungs-App gliedert sich sauber über vier APIs: IndexedDB für strukturierte Session- und Chunk-Metadaten (transaktional, asynchron, typisiert), die File System Access API für das direkte Lesen und Schreiben von Dateien mehrerer Gigabyte auf der Nutzerfestplatte, die Cache-API für HTTP-Antworten und App-Shell-Assets, und die Storage Manager API für Quota-Verwaltung und Persistenz-Hinweise.

Das Origin Private File System (OPFS) ergänzt diese für sandboxed Hochleistungs-I/O. Die Wahl der richtigen API ist entscheidend, weil Limits von 1 GB auf iOS Safari bis zu „60 % des freien Speichers" auf Desktop-Chrome reichen — und die falsche Wahl führt irgendwann zu QuotaExceededError in der Produktion.

Jede API der richtigen Aufgabe zuordnen

IndexedDB verwenden für alles, das wie eine Tabellenzeile aussieht: Upload-Sessions, abgeschlossene Chunk-Indizes, Share-Metadaten, Revocation-Tokens. Asynchron, transaktional, indizierbar und überlebt Sessions.

Die File System Access API nutzen, wenn Bytes auf die Festplatte geschrieben werden müssen, ohne die gesamte Datei in den RAM zu laden — ideal zum Speichern entschlüsselter Downloads über 500 MB. Chrome, Edge und Opera unterstützen sie vollständig; Firefox und Safari implementieren nur eine schreibgeschützte Teilmenge via showOpenFilePicker.

Die Cache-API für HTTP-Response-Objekte verwenden — das JS-Bundle, CSS, Icons und vielleicht gecachte API-Antworten. Sie ist für fetch-Abfangen in Service Workern optimiert.

OPFS (ein spezieller Origin-Private-Zweig der File System Access API) nutzen, wenn schneller, sandboxed, nicht sichtbarer Speicher gewünscht ist — zum Beispiel ein Schreibpuffer während eines Multi-Gigabyte-Verschlüsselungsdurchlaufs. Er erreicht Festplattendurchsatz um eine Größenordnung höher als IndexedDB für Binär-Blobs.

IndexedDB ohne die rauen Kanten

Die rohe IndexedDB-API hat eine berüchtigte, ereignisbasierte Schnittstelle. Jake Archibalds idb-Paket (1,5 KB gzip) oder Dexie.js (20 KB, reichere Abfrage-API) verwenden:

import { openDB } from 'idb';
const db = await openDB('transfers', 2, {
  upgrade(db, oldVersion) {
    if (oldVersion < 1) {
      const sessions = db.createObjectStore('sessions', { keyPath: 'id' });
      sessions.createIndex('by_expiry', 'expiresAt');
    }
    if (oldVersion < 2) {
      db.createObjectStore('chunks', { keyPath: ['sessionId', 'index'] });
    }
  }
});
await db.put('sessions', { id: 'abc', fileName: 'bericht.pdf', expiresAt: Date.now() + 86400000 });

Versionsmigrationen laufen im upgrade-Callback. Migrationen immer durch oldVersion sichern, damit Nutzer, die von v1 auf v3 springen, beide Schritte erhalten.

IndexedDB verarbeitet die meisten Datenformen, einschließlich Blobs und File-Referenzen, via strukturiertem Klon. Das bedeutet, Sie können ein File-Handle in einem Session-Datensatz speichern und die ursprünglichen Datei-Bytes nach einem Tab-Neuladen wieder lesen — perfekt für wiederaufnehmbare Uploads.

File System Access API für große Downloads

Die API ermöglicht, einen beschreibbaren Stream an den Browser-Speicherdialog zu übergeben:

const handle = await window.showSaveFilePicker({
  suggestedName: 'entschluesselt-archiv.zip',
  types: [{ description: 'Zip', accept: { 'application/zip': ['.zip'] } }]
});
const writable = await handle.createWritable();
await entschlüsselungsStream.pipeTo(writable);

Bytes fließen direkt auf die Festplatte, ohne je im JS-Heap zu landen. Das ist der einzige praktische Weg, eine 10 GB entschlüsselte Datei im Browser zu speichern.

Für Firefox und Safari auf StreamSaver.js zurückfallen, das einen Service Worker nutzt, um eine Streaming-Antwort zu synthetisieren, die das Download-UI auslöst. Gleiche Ergonomik, etwas mehr bewegliche Teile.

Persistente Datei-Handles ermöglichen es einer App auch, Dateien über Sessions hinweg wieder zu öffnen. Sobald ein Nutzer Zugriff via showOpenFilePicker gewährt hat, kann das FileSystemFileHandle in IndexedDB gespeichert und später mit handle.requestPermission() reaktiviert werden, ohne für jede Datei erneut zu fragen.

OPFS für Scratch-Bereich

Das Origin Private File System ist ein per-Origin sandboxed Speicher, der sich wie ein Dateisystem verhält, aber für den Nutzer nicht sichtbar ist:

const root = await navigator.storage.getDirectory();
const fh = await root.getFileHandle('scratch.bin', { create: true });
const access = await fh.createSyncAccessHandle(); // nur in Workern
access.write(buffer, { at: offset });
access.flush();
access.close();

createSyncAccessHandle ist nur innerhalb von Web Workern (einschließlich Service Workern) verfügbar. Es ist synchron und extrem schnell — Benchmarks zeigen 3–10-fache IndexedDB-Geschwindigkeit für sequentielle Schreibvorgänge. Verwenden, um einige Hundert Megabyte Verschlüsselungsausgabe vor dem Upload zu puffern, oder um eine entschlüsselte Arbeitskopie zu cachen, ohne den Downloads-Ordner des Nutzers zu belasten.

Safari 17 lieferte OPFS mit synchronen Zugriffs-Handles; Firefox 111 folgte. Alle drei großen Browser unterstützen es nun, was es für Produktionscode geeignet macht.

Speicherquoten und wie man sie überlebt

Alle APIs teilen denselben Origin-Quota-Pool. Ungefähre Obergrenzen:

  • Desktop Chrome: 60 % des freien Speichers
  • Desktop Firefox: 50 % des freien Speichers, standardmäßig auf 2 GB pro Origin begrenzt
  • Desktop Safari: 1 GB Warnung, wächst auf ~20 % des Speichers mit Nutzergenehmigung
  • iOS Safari: 1 GB pro Origin, aggressive 7-Tage-Eviction bei Nichtverwendung
  • Chrome Android: 10 % des freien Speichers, Eviction bei Druck

Quota zur Laufzeit prüfen:

const { quota, usage } = await navigator.storage.estimate();
console.log(`${(usage/1e9).toFixed(2)} GB von ${(quota/1e9).toFixed(2)} GB belegt`);

Persistenz für kritische Stores anfordern:

const persisted = await navigator.storage.persist();

Gibt true zurück, wenn der Browser persistenten Speicher gewährt hat — also keinen Druck-Eviction durchführen wird. Chrome gewährt das automatisch für Seiten, mit denen der Nutzer viel interagiert hat; Firefox fragt nach.

Cache-API für App-Shell und Offline

Die Cache-API speichert Request+Response-Paare und ist die richtige Wahl innerhalb von Service Workern:

const cache = await caches.open('shell-v7');
await cache.addAll([
  '/', '/app.js', '/app.css', '/icons/192.png'
]);

Beim Abfangen abrufen:

self.addEventListener('fetch', (e) => {
  e.respondWith(caches.match(e.request).then(r => r ?? fetch(e.request)));
});

Keine verschlüsselten Datei-Bytes in der Cache-API speichern. Ein 2-GB-Response-Objekt überschreitet Quotas auf iOS Safari in einem Schritt und kann danach nicht range-abgerufen werden. Bytes gehören in OPFS oder direkt auf die Festplatte via File System Access.

Eviction und Datenverlust souverän handhaben

Nicht-persistenter Speicher wird gelöscht — das muss eingeplant werden. iOS Safari löscht nach 7 Tagen ohne Nutzung, unabhängig von der Quota. Chrome löscht nur, wenn Festplatten-Druck wirklich vorhanden ist. Firefox löscht zuletzt verwendete Origins, wenn der Quota-Pool voll wird.

Zwei Verteidigungsmuster:

  • Jeden Zustand, der nicht neu erstellt werden kann (Upload-Session-IDs, partielle Chunk-Offsets), in einem neuladefreundlichen Format schreiben, sodass ein frischer Seitenaufruf vom Server neu abrufen und fortfahren kann.
  • Für langlebigen Zustand navigator.storage.persist() anfordern und eine UI für Nutzer zeigen, die bestätigen können, wenn der Browser fragt.

Den Server als Quelle der Wahrheit für alles behandeln, was nicht verloren gehen darf. Browser-Speicher als schnellen Cache betrachten, der über Nacht verschwinden könnte.

Browser-Kompatibilitätsfallen

Drei Fallen tauchen immer wieder auf:

  1. indexedDB.databases()-Auflistung wird in Firefox nicht unterstützt (Nutzer, die „Cookies beim Schließen löschen" aktiviert haben, verlieren alle IndexedDB-Inhalte ohne Ereignisauslösung).
  2. FileSystemFileHandle.queryPermission() verhält sich nach Neuladen anders — gibt manchmal 'prompt' zurück, obwohl die Berechtigung bereits erteilt wurde. Immer defensiv requestPermission() aufrufen.
  3. Privater / Inkognito-Modus gibt allen drei APIs eine separate, kleinere, sitzungsgebundene Quota. Code, der im normalen Browsing funktioniert, kann im privaten Fenster sofort QuotaExceededError treffen.

HexaTransfer nutzt IndexedDB für Session-Zustand, OPFS für Geheimtext-Pufferung während des Streaming-Verschlüsselns und die File System Access API für 10-GB-entschlüsselte Downloads auf unterstützten Browsern. Testen Sie es unter https://hexatransfer.com — kostenlos, kein Konto, bis 10 GB.

Den richtigen Stack für Ihre App wählen

Für die meisten Transfer-Apps ist die richtige Kombination: idb-Wrapper über IndexedDB für Metadaten, OPFS-Synchronzugriffs-Handles für Verschlüsseln/Entschlüsseln-Scratch-Bereich, Cache-API für die App-Shell innerhalb eines Service Workers, File System Access API für finale Downloads mit StreamSaver-Fallback, und ein navigator.storage.persist()-Aufruf beim Onboarding. Das deckt jeden heute ausgelieferten Browser ab, bleibt unter Quoten auf Mobilgeräten und erholt sich sauber, wenn etwas gelöscht wird. Kleine Adapter um jede API bauen — so wird die Einführung einer neuen OPFS-Methode oder eine erhöhte Safari-Quota zu einer Datei-Änderung und einem Deploy.

Große Dateien sicher mit Ende-zu-Ende-Verschlüsselung senden

Übertragen Sie Dateien bis zu 10 GB kostenlos mit Ende-zu-Ende-Verschlüsselung. Kein Konto erforderlich. Ihre Dateien werden in Ihrem Browser verschlüsselt, bevor sie hochgeladen werden — niemand sonst kann sie lesen.

Datei senden