Start · Sprachen · JavaScript · Referenz · FileSystemObserver

FileSystemObserver

Schnittstelle

Beobachtet Änderungen an Dateien und Verzeichnissen im benutzerbezogenen Dateisystem und im Origin Private File System (OPFS) und meldet sie gesammelt an eine Callback-Funktion.

seit JavaScript Experimentell: Chromium 133+ (Chrome, Edge); nicht Kategorie: webapi

Signatur

interface FileSystemObserver

Beschreibung

Das Interface FileSystemObserver gehört zur File System API. Es funktioniert ähnlich wie MutationObserver oder ResizeObserver: Man erzeugt mit new FileSystemObserver(callback) eine Instanz und meldet über observe() einzelne Handles an, also FileSystemFileHandle, FileSystemDirectoryHandle oder FileSystemSyncAccessHandle. Wenn sich dort etwas ändert, ruft der Browser die Callback-Funktion mit einem Array von FileSystemChangeRecord-Objekten auf.

Einsatzgebiete sind zum Beispiel Web-Editoren und IDEs, die eine Datei neu laden sollen, sobald ein anderes Programm sie verändert hat. Auch Foto- oder Medienverwaltungen, die einen Ordner synchron halten, und Anwendungen, die Änderungen am OPFS über mehrere Tabs oder Worker hinweg mitbekommen müssen, profitieren davon. Ohne diesen Observer müsste man das Dateisystem regelmäßig per Polling abfragen, was aufwendig und fehleranfällig ist.

Die Instanz hat drei Methoden:

  • observe(handle, options): Startet die Beobachtung und gibt ein Promise<undefined> zurück. Mit { recursive: true } werden bei Verzeichnissen auch alle Unterordner beobachtet.
  • unobserve(handle): Beendet die Beobachtung eines einzelnen Handles.
  • disconnect(): Beendet alle Beobachtungen dieser Instanz.

Jeder FileSystemChangeRecord hat folgende Eigenschaften: type ("appeared", "disappeared", "modified", "moved", "unknown" oder "errored"), changedHandle, root (das beobachtete Handle), relativePathComponents (der Pfad relativ zu root als Array) und bei Verschiebungen zusätzlich relativePathMovedFrom.

Parameter

Name Typ Default Beschreibung
$callback Pflicht (records: FileSystemChangeRecord[], observer: FileSystemObserver) => void Parameter des Konstruktors. Wird mit einem Array von Änderungsdatensätzen und der auslösenden Observer-Instanz aufgerufen.
$handle Pflicht FileSystemFileHandle | FileSystemDirectoryHandle | FileSystemSyncAccessHandle Parameter von observe() und unobserve(): das Handle, das beobachtet werden soll bzw. nicht mehr beobachtet werden soll.
$options.recursive boolean false Option von observe(): Bei true werden auch Änderungen in allen Unterverzeichnissen gemeldet.

Beispiele

Ausgewählten Ordner rekursiv beobachten

if (!('FileSystemObserver' in self)) {
  console.warn('FileSystemObserver wird nicht unterstützt');
} else {
  const callback = (records, observer) => {
    for (const record of records) {
      const path = record.relativePathComponents.join('/');
      console.log(`${record.type}: ${path || '(Wurzel)'}`);
    }
  };

  const observer = new FileSystemObserver(callback);

  document.querySelector('#ordner-waehlen').addEventListener('click', async () => {
    const dirHandle = await window.showDirectoryPicker();
    await observer.observe(dirHandle, { recursive: true });
    console.log(`Beobachte „${dirHandle.name}“`);
  });
}
Beobachte „projekt“ modified: src/index.js appeared: src/neu.txt

Editor: Datei bei externer Änderung neu laden

const [fileHandle] = await window.showOpenFilePicker();
const editor = document.querySelector('textarea');

const reload = async () => {
  const file = await fileHandle.getFile();
  editor.value = await file.text();
};

await reload();

const observer = new FileSystemObserver(async (records) => {
  for (const { type } of records) {
    if (type === 'modified') {
      await reload();
      console.log('Datei extern geändert – neu geladen');
    } else if (type === 'disappeared') {
      console.warn('Datei wurde gelöscht');
      observer.unobserve(fileHandle);
    }
  }
});

await observer.observe(fileHandle);
Datei extern geändert – neu geladen

OPFS beobachten, Verschiebungen auswerten und aufräumen

const root = await navigator.storage.getDirectory();

const observer = new FileSystemObserver((records) => {
  for (const record of records) {
    if (record.type === 'moved') {
      console.log(
        `verschoben: ${record.relativePathMovedFrom.join('/')} → ${record.relativePathComponents.join('/')}`
      );
    } else if (record.type === 'errored' || record.type === 'unknown') {
      // Ereignisse sind verloren gegangen: Zustand komplett neu einlesen
      console.warn('Beobachtung unzuverlässig, bitte neu synchronisieren');
    } else {
      console.log(record.type, record.relativePathComponents.join('/'));
    }
  }
});

await observer.observe(root, { recursive: true });

// Datei anlegen – löst "appeared" aus
const file = await root.getFileHandle('notizen.txt', { create: true });
const writable = await file.createWritable();
await writable.write('Hallo');
await writable.close();

// Später: alle Beobachtungen beenden
setTimeout(() => observer.disconnect(), 5000);
appeared notizen.txt modified notizen.txt

// Wichtig · Fallstricke

  • Experimentell: Die API ist bisher nur in Chromium-basierten Browsern verfügbar (vorher als Origin Trial) und noch nicht standardisiert. Deshalb immer per 'FileSystemObserver' in self prüfen, ob sie vorhanden ist, und notfalls auf Polling ausweichen.
  • Sicherer Kontext: Die API funktioniert nur über HTTPS bzw. localhost. Für das lokale Dateisystem braucht es Handles, die der Nutzer über showOpenFilePicker() oder showDirectoryPicker() freigegeben hat. Wird die Berechtigung entzogen, endet die Beobachtung, meist mit einem Datensatz vom Typ "errored".
  • Datensätze vom Typ "unknown": Das Betriebssystem kann Ereignisse zusammenfassen oder verwerfen. Bei "unknown" oder "errored" sollte die Anwendung den Zustand vollständig neu einlesen und sich nicht darauf verlassen, jede einzelne Änderung mitbekommen zu haben.
  • Ereignisse werden gesammelt und zeitversetzt geliefert. Viele Schreibvorgänge kurz hintereinander können mehrere "modified"-Datensätze auslösen. Aufwendige Reaktionen wie das Neuladen einer Datei sollte man deshalb bündeln (Debounce).
  • Eine rekursive Beobachtung großer Verzeichnisbäume kann viele Systemressourcen belegen. Nicht mehr benötigte Beobachtungen sollte man mit unobserve() oder disconnect() beenden.
  • Die API ist auch in Web Workern verfügbar. Dort lässt sich ein FileSystemSyncAccessHandle beobachten.