Signatur
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 einPromise<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}“`);
});
}
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);
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);
// 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 selfprü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 übershowOpenFilePicker()odershowDirectoryPicker()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()oderdisconnect()beenden. - Die API ist auch in Web Workern verfügbar. Dort lässt sich ein
FileSystemSyncAccessHandlebeobachten.