File System Access API praktisch nutzen: Dateien direkt im Browser lesen und schreiben
AI generated
JS
() =>
JavaScript · Browser APIs · Dateisystem
File System Access API praktisch nutzen
Dateien direkt im Browser lesen und schreiben

Die File System Access API erlaubt Webanwendungen echten Lese- und Schreibzugriff auf lokale Dateien und Ordner, ohne den klassischen Umweg über Datei-Uploads und Downloads. Wer Editoren, Bildwerkzeuge oder Datenexporte im Browser baut, erhält damit ein Verhalten, das sich fast wie eine native Desktop-Anwendung anfühlt, inklusive Berechtigungsmodell und persistenten Handles.

18 Min. Lesezeit showOpenFilePicker · showSaveFilePicker · showDirectoryPicker Chrome · Edge · Opera (Secure Context)

1. Warum die File System Access API existiert

Vor der File System Access API war der Umgang mit lokalen Dateien im Browser ein Kompromiss. Ein <input type="file"> lieferte eine einmalige Momentaufnahme einer Datei als File-Objekt, jede Änderung musste über einen neuen Download als frische Kopie an den Nutzer zurückgegeben werden. Es gab keine Möglichkeit, dieselbe Datei erneut zu öffnen, in sie hineinzuschreiben oder ein Verzeichnis strukturiert zu durchsuchen. Für Text-Editoren, Bildbearbeitung oder Entwicklerwerkzeuge im Browser war das ein spürbarer Rückstand gegenüber nativen Anwendungen.

Die File System Access API schließt genau diese Lücke. Sie stellt drei zentrale Einstiegspunkte bereit, window.showOpenFilePicker(), window.showSaveFilePicker() und window.showDirectoryPicker(), die jeweils einen nativen Dateidialog öffnen und ein FileSystemHandle zurückgeben. Über dieses Handle lässt sich eine Datei wiederholt lesen, überschreiben oder ein ganzes Verzeichnis rekursiv durchlaufen, ohne dass der Nutzer bei jedem Schritt erneut einen Dialog bestätigen muss. Das macht Web-Editoren wie VS Code for the Web oder Photopea erst praktisch nutzbar.

Wichtig ist, die File System Access API nicht mit der älteren, längst zurückgezogenen File and Directory Entries API zu verwechseln, die nur read-only Zugriff über webkitdirectory bot. Die neue API ist bewusst als Standard bei der WHATWG entstanden und wird kontinuierlich um Fähigkeiten wie synchronen Zugriff aus Web Workern erweitert, was sie auch für performance-kritische Anwendungen wie WebAssembly-Datenbanken interessant macht.

2. Sicherheitsmodell: Secure Context, Geste und Herkunft

Weil die File System Access API echten Schreibzugriff auf das lokale Dateisystem gewährt, ist ihr Berechtigungsmodell strenger als bei den meisten anderen Browser APIs. Jeder Aufruf von showOpenFilePicker, showSaveFilePicker oder showDirectoryPicker muss aus einer aktiven Nutzergeste heraus erfolgen, ein Klick oder Tastendruck. Ein Aufruf aus einem setTimeout oder direkt beim Laden der Seite wird vom Browser mit einem SecurityError abgelehnt.

Zusätzlich funktioniert die File System Access API ausschließlich in einem Secure Context, also über HTTPS oder auf localhost. Innerhalb eines <iframe> ist der Zugriff standardmäßig deaktiviert, es sei denn, das Elternfenster erlaubt es explizit über die Permissions-Policy fullscreen-artige Direktive window-management beziehungsweise die relevante Feature-Policy für Dateisystemzugriff. Diese Kombination aus Nutzergeste, Secure Context und expliziter Erlaubnis verhindert, dass eine eingebettete Werbung oder ein Drittanbieter-Skript unbemerkt Dateien öffnet.

Jede erteilte Berechtigung ist zudem an die konkrete Datei oder das konkrete Verzeichnis gebunden, nicht an die gesamte Festplatte. Ein Nutzer erlaubt gezielt den Zugriff auf ein Projektverzeichnis, nicht auf sein gesamtes Home-Verzeichnis. Das Sicherheitsmodell der File System Access API folgt damit demselben Prinzip wie bei Kamera oder Standort, granular, widerrufbar und immer sichtbar für den Nutzer über die Adressleisten-Berechtigungen.

3. Dateien öffnen mit showOpenFilePicker

Der Einstieg in die File System Access API gelingt am einfachsten über showOpenFilePicker(). Die Methode ist asynchron, gibt ein Array von FileSystemFileHandle-Objekten zurück und akzeptiert Optionen wie types zur Einschränkung auf bestimmte Dateiendungen und multiple für Mehrfachauswahl. Aus dem Handle lässt sich über getFile() jederzeit ein aktuelles File-Objekt anfordern, das sich dann wie gewohnt mit text(), arrayBuffer() oder stream() lesen lässt.

Der entscheidende Unterschied zum klassischen <input type="file">: Das Handle bleibt gültig, solange die Seite offen ist oder solange es persistiert wurde. Eine Anwendung kann also dasselbe Handle später erneut verwenden, um die Datei neu einzulesen, etwa nachdem der Nutzer sie extern in einem anderen Programm geändert hat. Genau dieses Verhalten macht Editoren im Browser praxistauglich.


// Open a text file and read its current content
async function openTextFile() {
  try {
    const [fileHandle] = await window.showOpenFilePicker({
      types: [
        {
          description: "Text files",
          accept: { "text/plain": [".txt", ".md"] },
        },
      ],
      multiple: false,
    });

    const file = await fileHandle.getFile();
    const contents = await file.text();

    console.log(`Opened ${file.name}, ${file.size} bytes`);

    // Keep the handle around so we can write back to the
    // exact same file later without asking again.
    return { fileHandle, contents };
  } catch (err) {
    if (err.name === "AbortError") {
      console.log("User cancelled the file picker");
      return null;
    }
    throw err;
  }
}

4. Dateien schreiben mit showSaveFilePicker

Zum Schreiben stellt die File System Access API showSaveFilePicker() bereit. Anders als beim klassischen <a download>-Muster fragt der Browser den Zielort einmalig ab und liefert danach ein Handle, über das beliebig oft geschrieben werden kann, ohne dass jedes Mal ein Speicherdialog erscheint. Zum eigentlichen Schreiben öffnet man mit createWritable() einen FileSystemWritableFileStream, schreibt Daten hinein und schließt den Stream mit close() ab, erst dieser Abschluss macht die Änderungen dauerhaft sichtbar.

Ein häufiger Fehler bei der File System Access API ist, den Stream nicht zu schließen, etwa weil eine Exception den close()-Aufruf überspringt. Die Datei bleibt dann in einem inkonsistenten Zustand, meist als leere temporäre Datei sichtbar. Ein try/finally-Block um den Schreibvorgang stellt sicher, dass der Stream in jedem Fall geschlossen wird, ähnlich wie man es von Dateizugriffen in anderen Sprachen kennt.


// Save (or overwrite) a file through the File System Access API
async function saveTextFile(fileHandle, content) {
  // Ask for a location only if we don't already have a handle
  if (!fileHandle) {
    fileHandle = await window.showSaveFilePicker({
      suggestedName: "notes.txt",
      types: [
        { description: "Text file", accept: { "text/plain": [".txt"] } },
      ],
    });
  }

  const writable = await fileHandle.createWritable();
  try {
    await writable.write(content);
  } finally {
    // Without close(), changes never reach the actual file on disk.
    await writable.close();
  }

  return fileHandle;
}

5. Verzeichnisse durchsuchen mit showDirectoryPicker

Für Anwendungen, die ganze Projektstrukturen im Browser abbilden wollen, bringt die File System Access API mit showDirectoryPicker() einen dritten Einstiegspunkt. Die Methode liefert ein FileSystemDirectoryHandle, das sich mit for await (const [name, handle] of directoryHandle.entries()) asynchron durchlaufen lässt. Jeder Eintrag ist entweder ein FileSystemFileHandle oder ein weiteres FileSystemDirectoryHandle, was rekursives Durchsuchen ganzer Verzeichnisbäume ermöglicht.

Diese Fähigkeit der File System Access API ist die Grundlage für browserbasierte IDEs, die ein komplettes Projektverzeichnis öffnen, alle Dateien indexieren und Änderungen direkt auf der Festplatte speichern, ohne ein Backend-Dateisystem zu benötigen. Die Methode getFileHandle(name, { create: true }) beziehungsweise getDirectoryHandle(name, { create: true }) erlaubt es zusätzlich, neue Dateien und Unterordner innerhalb eines bereits autorisierten Verzeichnisses anzulegen, ohne dafür erneut einen Dialog zu öffnen.


// Recursively list every file in a directory tree
async function listAllFiles(directoryHandle, path = "") {
  const files = [];

  for await (const [name, handle] of directoryHandle.entries()) {
    const fullPath = path ? `${path}/${name}` : name;

    if (handle.kind === "file") {
      files.push(fullPath);
    } else if (handle.kind === "directory") {
      // Recurse into subdirectories without asking the user again
      const nested = await listAllFiles(handle, fullPath);
      files.push(...nested);
    }
  }

  return files;
}

const rootHandle = await window.showDirectoryPicker();
const allFiles = await listAllFiles(rootHandle);
console.log(`Found ${allFiles.length} files`);

6. Handles persistieren und Berechtigungen erneuern

Ein Handle der File System Access API ist ein strukturierter, klonbarer Wert und lässt sich daher direkt in IndexedDB speichern. Damit kann eine Anwendung beim nächsten Seitenaufruf denselben Projektordner ohne erneuten Dateidialog wieder öffnen, sofern der Nutzer die Berechtigung nicht zwischenzeitlich widerrufen hat. Genau hier liegt der entscheidende Unterschied zur bloßen Referenz: Der Browser speichert die Berechtigung nicht dauerhaft automatisch, sie muss über queryPermission() geprüft und bei Bedarf über requestPermission() erneuert werden.

Diese erneute Anfrage erfordert wiederum eine Nutzergeste, kann also nicht beim automatischen Seitenladen im Hintergrund erfolgen. Die übliche Lösung: Beim Start prüft die Anwendung stillschweigend mit queryPermission({ mode: "readwrite" }), ob die Berechtigung noch besteht, und zeigt nur dann einen Button "Zugriff erneut erlauben" an, wenn der Status "prompt" statt "granted" zurückkommt. Das vermeidet unnötige Dialoge und erklärt dem Nutzer gleichzeitig, warum eine Aktion nötig ist.


// Persist a handle in IndexedDB and re-request permission later
const DB_NAME = "file-handles";
const STORE_NAME = "handles";

async function saveHandle(key, handle) {
  const db = await openHandleDb();
  const tx = db.transaction(STORE_NAME, "readwrite");
  tx.objectStore(STORE_NAME).put(handle, key);
  await tx.done;
}

async function restoreHandle(key) {
  const db = await openHandleDb();
  const handle = await db.transaction(STORE_NAME).objectStore(STORE_NAME).get(key);
  if (!handle) return null;

  const status = await handle.queryPermission({ mode: "readwrite" });
  if (status === "granted") return handle;

  // Re-requesting permission always needs a user gesture (e.g. click)
  const granted = await handle.requestPermission({ mode: "readwrite" });
  return granted === "granted" ? handle : null;
}

7. Große Dateien und Streaming-Schreibzugriff

Ein oft übersehener Vorteil der File System Access API gegenüber Blob-Downloads liegt bei großen Dateien. Ein klassischer Download über URL.createObjectURL() benötigt den gesamten Dateiinhalt zunächst als Blob im Arbeitsspeicher, bevor er geschrieben werden kann. Der FileSystemWritableFileStream dagegen ist ein echter WritableStream, der Daten häppchenweise auf die Festplatte schreibt, während sie erzeugt werden, etwa direkt aus einem Netzwerk-Response via pipeTo().

Für Video-Exporte, Datenbank-Dumps oder große CSV-Exporte reduziert das den Speicherbedarf drastisch, weil nie der komplette Inhalt gleichzeitig im RAM liegen muss. Zusätzlich unterstützt die File System Access API in Web Workern seit einiger Zeit den synchronen Zugriff über createSyncAccessHandle(), der für Datei-basierte SQLite-Kompilate wie wa-sqlite spürbar schneller ist als der asynchrone Pfad, weil er ohne Promise-Overhead direkt blockierend liest und schreibt.

8. Fallback-Strategien für Safari und Firefox

Safari und Firefox unterstützen die File System Access API bislang nicht vollständig, weshalb produktiver Code stets eine Feature-Erkennung benötigt. Die zuverlässigste Prüfung ist "showOpenFilePicker" in window, da reine User-Agent-Sniffing-Ansätze bei Browser-Updates schnell veralten. Fehlt die API, greift man auf das klassische Muster mit <input type="file"> zum Lesen und einem programmatisch erzeugten <a download>-Link zum Speichern zurück.

Wichtig ist, dass der Fallback dieselbe äußere Schnittstelle bereitstellt, damit der restliche Anwendungscode nicht zwischen zwei APIs unterscheiden muss. Eine kleine Abstraktionsschicht, die entweder ein echtes FileSystemFileHandle oder ein simuliertes Objekt mit denselben Methodennamen zurückgibt, hält den Rest der Codebasis frei von Verzweigungen. Bibliotheken wie browser-fs-access implementieren genau dieses Muster bereits fertig und sind für viele Projekte der pragmatischere Weg als eine Eigenentwicklung.


// Feature detection with a graceful fallback
async function pickAndSaveFile(content, suggestedName) {
  if ("showSaveFilePicker" in window) {
    const handle = await window.showSaveFilePicker({ suggestedName });
    const writable = await handle.createWritable();
    await writable.write(content);
    await writable.close();
    return;
  }

  // Fallback for Safari/Firefox: classic download link
  const blob = new Blob([content], { type: "text/plain" });
  const url = URL.createObjectURL(blob);
  const link = document.createElement("a");
  link.href = url;
  link.download = suggestedName;
  link.click();
  URL.revokeObjectURL(url);
}

9. File System Access API im Vergleich zu klassischen Mustern

Die Entscheidung zwischen der File System Access API und den klassischen Browser-Mustern hängt stark vom Anwendungsfall ab. Für einmalige Uploads reicht oft weiterhin ein simples <input type="file">, für wiederholtes Lesen und Schreiben derselben Datei ist die neue API jedoch klar überlegen.

Anforderung Klassisches Muster File System Access API Vorteil
Datei erneut öffnen Nicht möglich ohne neuen Upload FileSystemFileHandle Persistentes Handle statt Momentaufnahme
In dieselbe Datei schreiben Nur Download als neue Kopie createWritable() Überschreiben statt Duplizieren
Verzeichnis durchsuchen webkitdirectory (read-only) showDirectoryPicker() Lesen und Schreiben rekursiv
Große Dateien Blob komplett im RAM Streaming-Schreibzugriff Geringerer Speicherbedarf
Browser-Support Alle Browser Nur Chromium-Basis Fallback zwingend nötig

In der Praxis kombinieren erfolgreiche Anwendungen beide Ansätze: Die File System Access API wird genutzt, wo sie verfügbar ist, während der klassische Upload/Download-Weg als Fallback für Safari und Firefox erhalten bleibt. Diese doppelte Strategie sorgt dafür, dass die Anwendung überall funktioniert, während Chromium-Nutzer von der komfortableren, direkteren Dateiverwaltung profitieren.

Mironsoft

JavaScript-Entwicklung, Browser APIs und moderne Web-Anwendungen

Editor- oder Datei-Feature im Browser geplant?

Wir integrieren die File System Access API in eure Web-Anwendung, inklusive Berechtigungsmodell, Persistenz über IndexedDB und robustem Fallback für Safari und Firefox.

API-Integration

showOpenFilePicker, showSaveFilePicker und showDirectoryPicker sauber angebunden

Fallback-Konzept

Robuste Feature-Detection und Upload/Download-Alternative für alle Browser

Performance-Tuning

Streaming-Schreibzugriff für große Dateien statt Blob im Arbeitsspeicher

10. Zusammenfassung

Die File System Access API hebt den Umgang mit lokalen Dateien im Browser auf ein Niveau, das sich fast wie eine native Anwendung anfühlt. Statt einmaliger Uploads und neuer Downloads bei jeder Änderung erlaubt sie persistente FileSystemHandle-Objekte, die wiederholtes Lesen, Schreiben und rekursives Durchsuchen von Verzeichnissen ermöglichen. Das Sicherheitsmodell mit Nutzergeste, Secure Context und granularen Berechtigungen sorgt dafür, dass diese Mächtigkeit nicht zum Einfallstor für unbemerkten Dateizugriff wird.

Wer die File System Access API produktiv einsetzt, sollte drei Dinge beachten: Handles konsequent in IndexedDB persistieren, Berechtigungen bei jedem Seitenaufruf über queryPermission() prüfen statt blind anzunehmen, und immer eine Fallback-Strategie für Safari und Firefox bereithalten. Mit dieser Kombination lassen sich Web-Editoren, Datei-Manager und Exportwerkzeuge bauen, die in unterstützten Browsern das volle native Erlebnis bieten und überall sonst zuverlässig degradieren.

File System Access API praktisch nutzen — Das Wichtigste auf einen Blick

Drei Einstiegspunkte

showOpenFilePicker, showSaveFilePicker und showDirectoryPicker liefern jeweils ein persistentes FileSystemHandle.

Berechtigungsmodell

Jeder Aufruf braucht eine Nutzergeste und einen Secure Context. Berechtigungen sind pro Datei granular und widerrufbar.

Persistenz

Handles in IndexedDB speichern, Berechtigung beim nächsten Aufruf mit queryPermission() prüfen statt neu erfragen.

Fallback

Feature-Detection mit "showOpenFilePicker" in window, Upload/Download-Muster für Safari und Firefox bereithalten.

11. FAQ: File System Access API praktisch nutzen

1Was ist die File System Access API?
Echter Lese- und Schreibzugriff auf lokale Dateien über showOpenFilePicker, showSaveFilePicker und showDirectoryPicker mit persistenten Handles.
2Welche Browser unterstützen sie?
Chrome, Edge, Opera vollständig. Safari und Firefox nicht, ein Fallback ist zwingend nötig.
3Warum eine Nutzergeste nötig?
Verhindert, dass Skripte ungefragt beim Laden der Seite Dateidialoge öffnen.
4Handle über Seitenaufruf hinaus speichern?
Ja, in IndexedDB. Berechtigung beim nächsten Aufruf über queryPermission erneut prüfen.
5Stream nicht geschlossen, was passiert?
Änderungen landen nie in der Datei. try/finally um close() nutzen.
6Sicher gegenüber böswilligen Skripten?
Ja, durch Secure Context, Nutzergeste und granulare, widerrufbare Berechtigungen pro Datei.
7Verzeichnis rekursiv durchsuchen?
showDirectoryPicker holen, mit entries() iterieren, bei kind directory rekursiv weitermachen.
8Für große Dateien geeignet?
Ja, Streaming-Schreibzugriff spart Arbeitsspeicher gegenüber Blob-Downloads.
9Fallback für Safari/Firefox bauen?
Feature-Prüfung, bei Fehlen klassisches input/download-Muster verwenden.
10Fertige Bibliotheken vorhanden?
browser-fs-access kapselt Feature-Detection und Fallback bereits vollständig.