Signatur
Beschreibung
FileSystemWritableFileStream gehört zur File System API. Du bekommst ein solches Objekt nicht über einen Konstruktor, sondern über FileSystemFileHandle.prototype.createWritable(). Das Handle stammt entweder aus window.showSaveFilePicker() bzw. showOpenFilePicker() (File System Access API, Dateien des Nutzers) oder aus dem Origin Private File System (navigator.storage.getDirectory()).
Die Schnittstelle erbt von WritableStream. Du kannst also mit getWriter() und pipeTo() arbeiten. Dazu kommen drei Komfortmethoden:
write(data): schreibt einArrayBuffer,TypedArray,DataView, einenBloboder einenStringan die aktuelle Position. Stattdessen geht auch ein Befehlsobjekt{ type: "write" | "seek" | "truncate", data, position, size }.seek(position): setzt den Dateizeiger auf ein bestimmtes Byte.truncate(size): kürzt oder verlängert die Datei aufsizeBytes.
Alles wird zuerst in eine temporäre Swap-Datei geschrieben. Erst close() überträgt die Änderungen atomar in die Zieldatei. Rufst du stattdessen abort() auf oder vergisst du close(), bleibt die Originaldatei unverändert. Das eignet sich für Editoren, Exportfunktionen und Web-Apps, die lokale Dateien speichern wollen, ohne jedes Mal einen Download auszulösen.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $data Pflicht | ArrayBuffer | TypedArray | DataView | Blob | string | {type: "write"|"seek"|"truncate", data?, position?: number, size?: number} | Parameter von write(): die Daten oder ein Befehlsobjekt. Bei type: "write" mit position wird ab diesem Byte geschrieben. |
|
| $position Pflicht | number | Parameter von seek(): Byte-Offset ab Dateianfang, auf den der Dateizeiger gesetzt wird. |
|
| $size Pflicht | number | Parameter von truncate(): neue Dateigröße in Bytes. Wird die Datei dadurch länger, wird mit Nullbytes aufgefüllt. |
Beispiele
Text über den Speichern-Dialog in eine Datei schreiben
const saveText = async (text) => {
// Muss durch eine Nutzeraktion ausgelöst werden (z. B. Klick)
const handle = await window.showSaveFilePicker({
suggestedName: 'notiz.txt',
types: [{ description: 'Textdatei', accept: { 'text/plain': ['.txt'] } }]
});
const writable = await handle.createWritable();
await writable.write(text);
await writable.close(); // erst jetzt landet der Inhalt in der Datei
console.log(`Gespeichert: ${handle.name}`);
};
document.querySelector('#save').addEventListener('click', () => saveText('Hallo Welt'));
Im Origin Private File System mit seek, truncate und Befehlsobjekten arbeiten
const root = await navigator.storage.getDirectory();
const handle = await root.getFileHandle('log.txt', { create: true });
// keepExistingData: true übernimmt den bisherigen Inhalt in die Swap-Datei
const writable = await handle.createWritable({ keepExistingData: true });
await writable.write('ABCDEFGHIJ');
await writable.seek(2);
await writable.write('xy'); // ABxyEFGHIJ
await writable.write({ type: 'write', position: 8, data: 'Z' }); // ABxyEFGHZJ
await writable.truncate(9); // ABxyEFGHZ
await writable.close();
const file = await handle.getFile();
console.log(await file.text());
Download per pipeTo() direkt in eine Datei streamen
const downloadTo = async (url) => {
const handle = await window.showSaveFilePicker({ suggestedName: 'video.mp4' });
const writable = await handle.createWritable();
const response = await fetch(url);
// pipeTo() schließt den Stream am Ende automatisch
await response.body.pipeTo(writable);
console.log('Download abgeschlossen');
};
Änderungen bei einem Fehler verwerfen
const root = await navigator.storage.getDirectory();
const handle = await root.getFileHandle('config.json', { create: true });
const writable = await handle.createWritable();
try {
const json = JSON.stringify({ theme: 'dark', version: 2 }, null, 2);
await writable.write(json);
await writable.close();
} catch (err) {
await writable.abort(); // Originaldatei bleibt unverändert
console.error('Speichern fehlgeschlagen:', err);
}
// Wichtig · Fallstricke
- Browser-Unterstützung:
showSaveFilePicker()undshowOpenFilePicker()gibt es nur in Chromium-Browsern (Chrome, Edge, Opera). Firefox und Safari unterstützencreateWritable()nur für Dateien im Origin Private File System. Prüfe vorher, ob die Funktion existiert, z. B. mit'showSaveFilePicker' in window. - Ohne
close()passiert nichts: Bisclose()aufgerufen wird, landet alles nur in der Swap-Datei. Schließt der Tab vorher, gehen die Daten verloren. - Bestehender Inhalt: Standardmäßig startet der Stream mit einer leeren Datei. Mit
createWritable({ keepExistingData: true })bleibt der bisherige Inhalt erhalten, etwa zum Anhängen oder Überschreiben einzelner Bytes. - Sperren: Solange ein Writable offen ist, kann es zu einer
NoModificationAllowedErrorkommen, z. B. wenn gleichzeitig einFileSystemSyncAccessHandleauf dieselbe Datei geöffnet ist. - Berechtigungen und Sicherheit: Die API funktioniert nur über HTTPS. Picker brauchen eine Nutzeraktion. Für Schreibzugriff auf Nutzerdateien muss die Berechtigung
readwriteerteilt sein (handle.requestPermission({ mode: 'readwrite' })). Chromium prüft die Swap-Datei beimclose()unter Umständen mit Safe Browsing. Deshalb kann das Schließen bei großen Dateien spürbar dauern. - Performance: Viele kleine
write()-Aufrufe sind langsam. Sammle die Daten lieber in einemBloboder verwendepipeTo(). Für sehr häufige, synchrone Zugriffe im Worker istFileSystemSyncAccessHandleschneller.