Signatur
Beschreibung
window.showSaveFilePicker() gehört zur File System Access API. Die Methode öffnet den Speichern-Dialog des Betriebssystems. Wählt die Person einen Speicherort und einen Dateinamen, wird das Promise mit einem FileSystemFileHandle erfüllt. Über dessen Methode createWritable() schreibst du Daten direkt in die Datei auf dem Datenträger. Einen Download-Link über <a download> brauchst du dafür nicht.
Die Methode eignet sich für webbasierte Editoren, Zeichenprogramme, Tabellen-Werkzeuge und ähnliche Anwendungen mit echtem „Speichern unter …“. Das zurückgegebene Handle kannst du behalten, zum Beispiel in IndexedDB. Spätere Speichervorgänge schreiben dann in dieselbe Datei, ohne dass der Dialog erneut erscheint. Eventuell musst du vorher mit requestPermission() die Berechtigung neu anfordern.
Mit dem Parameter options steuerst du den Dialog:
suggestedNameschlägt einen Dateinamen vor.typeslegt erlaubte Dateitypen fest (MIME-Typ plus Dateiendungen).startInbestimmt den Startordner.idsorgt dafür, dass sich der Browser den zuletzt benutzten Ordner pro Anwendungsfall merkt.
Die Methode funktioniert nur in einem sicheren Kontext (HTTPS oder localhost). Außerdem braucht sie eine Benutzeraktivierung, also zum Beispiel einen Klick.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $options | Object | {} | Konfigurationsobjekt für den Dialog. Mögliche Felder stehen in den folgenden Einträgen. |
| $options.suggestedName | string | Vorgeschlagener Dateiname, z. B. "bericht.txt". |
|
| $options.types | Array<{description?: string, accept: Record<string, string[]>}> | [] | Erlaubte Dateitypen. accept ordnet MIME-Typen den Dateiendungen zu, z. B. { "text/plain": [".txt"] }. |
| $options.excludeAcceptAllOption | boolean | false | Bei true fehlt im Dialog die Auswahl „Alle Dateien“. |
| $options.id | string | Kennung für den Anwendungsfall (max. 32 Zeichen, [A-Za-z0-9_-]). Der Browser merkt sich den zuletzt benutzten Ordner pro id. |
|
| $options.startIn | FileSystemHandle|"desktop"|"documents"|"downloads"|"music"|"pictures"|"videos" | Startordner des Dialogs: ein bekannter Systemordner oder ein vorhandenes Handle. |
Rückgabewert
FileSystemFileHandle für die gewählte Datei erfüllt. Bricht die Person den Dialog ab, wird das Promise mit einem AbortError (DOMException) abgelehnt. Fehlt die Benutzeraktivierung, gibt es einen SecurityError. Bei ungültigen types gibt es einen TypeError.Beispiele
Text als Datei speichern
const saveButton = document.querySelector('#save');
saveButton.addEventListener('click', async () => {
try {
const handle = await window.showSaveFilePicker({
suggestedName: 'notizen.txt',
types: [{
description: 'Textdateien',
accept: { 'text/plain': ['.txt'] }
}]
});
const writable = await handle.createWritable();
await writable.write('Hallo vom Web-Editor!');
await writable.close(); // erst jetzt landen die Daten auf der Platte
console.log(`Gespeichert als ${handle.name}`);
} catch (err) {
if (err.name === 'AbortError') {
console.log('Speichern abgebrochen');
} else {
console.error(err);
}
}
});
JSON-Export mit Fallback für Firefox und Safari
const saveJson = async (data, fileName = 'export.json') => {
const blob = new Blob([JSON.stringify(data, null, 2)], {
type: 'application/json'
});
if ('showSaveFilePicker' in window) {
const handle = await window.showSaveFilePicker({
suggestedName: fileName,
id: 'json-export',
startIn: 'documents',
types: [{
description: 'JSON-Datei',
accept: { 'application/json': ['.json'] }
}]
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return;
}
// Fallback: klassischer Download
const url = URL.createObjectURL(blob);
const a = Object.assign(document.createElement('a'), {
href: url,
download: fileName
});
a.click();
URL.revokeObjectURL(url);
};
document.querySelector('#export').addEventListener('click', () =>
saveJson({ nutzer: 'Anna', punkte: 42 })
);
„Speichern“ und „Speichern unter“ mit gemerktem Handle
let fileHandle = null;
const saveAs = async (content) => {
fileHandle = await window.showSaveFilePicker({
suggestedName: 'dokument.md',
types: [{ description: 'Markdown', accept: { 'text/markdown': ['.md'] } }]
});
await save(content);
};
const save = async (content) => {
if (!fileHandle) return saveAs(content);
const writable = await fileHandle.createWritable();
await writable.write(content);
await writable.close();
console.log('Datei aktualisiert:', fileHandle.name);
};
document.querySelector('#save').addEventListener('click', () =>
save(document.querySelector('textarea').value)
);
// Wichtig · Fallstricke
- Browser-Unterstützung: Die Methode gibt es nur in Chromium-basierten Desktop-Browsern (Chrome, Edge und Opera ab Version 86). Firefox, Safari und Android-Chrome unterstützen sie nicht. Prüfe deshalb immer mit
'showSaveFilePicker' in window, ob sie vorhanden ist, und biete einen Fallback über<a download>an. - Sicherer Kontext und Benutzeraktivierung: Die Methode läuft nur über HTTPS oder localhost. Sie muss direkt aus einer Nutzerinteraktion heraus aufgerufen werden, sonst gibt es einen
SecurityError. In Cross-Origin-iframes ist sie gesperrt. - Abbruch behandeln: Schließt die Person den Dialog, wird das Promise mit einem
AbortErrorabgelehnt. Fange diesen Fehler ab und zeige ihn nicht als echten Fehler an. - Schreibvorgang abschließen:
createWritable()schreibt zunächst in eine temporäre Datei. Erstclose()übernimmt die Änderungen in die Zieldatei. Vergisst duclose(), bleibt die Datei leer bzw. unverändert. - Datei wird schon beim Auswählen angelegt: Die Zieldatei kann bereits entstehen oder geleert werden, sobald ein Ort gewählt ist, also noch bevor du Daten schreibst.
- Gesperrte Orte: Systemordner und sensible Dateiendungen (z. B.
.lnk,.exe) blockiert der Browser möglicherweise. - Der Standard ist ein WICG-Entwurf (kein W3C-Standard) und kann sich noch ändern.