Die File System Access API ist eine Web-API, die Lese- und Schreibzugriff auf die lokalen Dateien eines Benutzers ermöglicht. Sie eröffnet neue Möglichkeiten für die Entwicklung leistungsfähiger Webanwendungen wie Texteditoren oder IDEs, Bildbearbeitungswerkzeuge, verbesserte Import-/Exportfunktionen, alles im Frontend. Schauen wir uns an, wie man mit dieser API beginnt.

Dateien mit der File System Access API lesen
Bevor wir uns mit dem Code zum Lesen einer Datei vom System des Benutzers befassen, ist es wichtig zu bedenken, dass der Aufruf der File System Access API durch eine Benutzerinteraktion in einem sicheren Kontext erfolgen muss. Im folgenden Beispiel verwenden wir ein Klickereignis.
Aus einer einzelnen Datei lesen
Das Lesen von Daten aus einer Datei kann in weniger als 10 Codezeilen erfolgen. Hier ist ein Beispielcode-Snippet.
let fileHandle;
document.querySelector(".pick-file").onclick = async () => {
[fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
const content = await file.text();
return content;
};
Stellen wir uns vor, wir haben einen Button in unserem HTML mit der Klasse .pick-file. Wenn wir auf diesen Button klicken, starten wir die Dateiauswahl, indem wir window.showOpenFilePicker() aufrufen, und speichern das Ergebnis dieser Abfrage in einer Variablen namens fileHandle.
Was wir nach dem Aufruf von showOpenFilePicker() zurückbekommen, ist ein Array von FileSystemFileHandle-Objekten, die jede von uns ausgewählte Datei repräsentieren. Da dieses Beispiel für eine einzelne Datei ist, destrukturieren wir das Ergebnis. Wie man mehrere Dateien auswählt, zeige ich später.
Diese Objekte enthalten die Eigenschaften kind und name. Wenn Sie console.log(fileHandle) aufrufen würden, würden Sie das folgende Objekt sehen:
FileSystemFileHandle {kind: 'file', name: 'data.txt'}
Die kind kann entweder file oder directory sein.
Auf fileHandle können wir dann die Methode getFile() aufrufen, um Details zu unserer Datei zu erhalten. Der Aufruf dieser Methode gibt ein Objekt mit einigen Eigenschaften zurück, darunter ein Zeitstempel, wann die Datei zuletzt geändert wurde, der Name der Datei, ihre Größe und ihr Typ.
Schließlich können wir text() auf der Datei aufrufen, um ihren Inhalt zu erhalten.
Aus mehreren Dateien lesen
Um aus mehreren Dateien zu lesen, müssen wir ein options-Objekt an showOpenFilePicker() übergeben.
Zum Beispiel:
let fileHandles;
const options = {
multiple: true,
};
document.querySelector(".pick-file").onclick = async () => {
fileHandles = await window.showOpenFilePicker(options);
// The rest of the code will be shown below
};
Standardmäßig ist die Eigenschaft multiple auf false gesetzt. Andere Optionen können verwendet werden, um die Arten von Dateien anzugeben, die ausgewählt werden können.
Wenn wir beispielsweise nur .jpeg-Dateien akzeptieren wollten, würde das Optionenobjekt Folgendes enthalten:
const options = {
types: [
{
description: "Images",
accept: {
"image/jpeg": ".jpeg",
},
},
],
excludeAcceptAllOption: true,
};
In diesem Beispiel enthält fileHandles ein Array mit mehreren Dateien. Das Abrufen ihres Inhalts würde also wie folgt erfolgen:
let fileHandles;
const options = {
multiple: true,
};
document.querySelector(".pick-file").onclick = async () => {
fileHandles = await window.showOpenFilePicker(options);
const allContent = await Promise.all(
fileHandles.map(async (fileHandle) => {
const file = await fileHandle.getFile();
const content = await file.text();
return content;
})
);
console.log(allContent);
};
In eine Datei mit der File System Access API schreiben
Die File System Access API ermöglicht auch das Schreiben von Inhalten in Dateien. Betrachten wir zunächst, wie eine neue Datei gespeichert wird.
In eine neue Datei schreiben
Das Schreiben in eine neue Datei kann ebenfalls mit sehr wenig Code erfolgen!
document.querySelector(".save-file").onclick = async () => {
const options = {
types: [
{
description: "Test files",
accept: {
"text/plain": [".txt"],
},
},
],
};
const handle = await window.showSaveFilePicker(options);
const writable = await handle.createWritable();
await writable.write("Hello World");
await writable.close();
return handle;
};
Wenn wir uns einen zweiten Button mit der Klasse save-file vorstellen, öffnen wir beim Klicken den Dateiauswahldialog mit der Methode showSaveFilePicker() und übergeben ein option-Objekt, das den zu speichernden Dateityp enthält, hier eine .txt-Datei.
Der Aufruf dieser Methode gibt ebenfalls ein FileSystemFileHandle-Objekt zurück, wie im ersten Abschnitt. Auf diesem Objekt können wir die Methode createWritable() aufrufen, die ein FileSystemWritableFileStream-Objekt zurückgibt. Anschließend können wir mit der Methode write(), der wir den Inhalt übergeben müssen, Inhalt in diesen Stream schreiben.
Schließlich müssen wir die Methode close() aufrufen, um die Datei zu schließen und das Schreiben des Inhalts auf die Festplatte abzuschließen.
Wenn Sie beispielsweise HTML-Code in eine Datei schreiben möchten, müssten Sie nur das options-Objekt ändern, um "text/html": [".html"] zu akzeptieren und den HTML-Inhalt an die write()-Methode zu übergeben.
Eine vorhandene Datei bearbeiten
Wenn Sie eine Datei importieren und mit der File System Access API bearbeiten möchten, würde ein Beispielcode-Snippet so aussehen:
let fileHandle;
document.querySelector(".pick-file").onclick = async () => {
[fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
const writable = await fileHandle.createWritable();
await writable.write("This is a new line");
await writable.close();
};
Wenn Sie dem Rest dieses Beitrags gefolgt sind, erkennen Sie vielleicht, dass wir mit den Methoden showOpenFilePicker() und getFile() beginnen, um eine Datei zu lesen, und dann createWritable(), write() und close() verwenden, um in dieselbe Datei zu schreiben.
Wenn die importierte Datei bereits Inhalt hat, ersetzt dieses Code-Snippet den aktuellen Inhalt durch den neuen, der an die write()-Methode übergeben wurde.
Zusätzliche Funktionen der File System Access API
Ohne ins Detail zu gehen, ermöglicht die File System Access API auch das Auflisten von Dateien in Verzeichnissen und das Löschen von Dateien oder Verzeichnissen.
Verzeichnisse lesen
Das Lesen von Verzeichnissen kann mit wenig Code erfolgen.
document.querySelector(".read-dir").onclick = async () => {
const directoryHandle = await window.showDirectoryPicker();
for await (const entry of directoryHandle.values()) {
console.log(entry.kind, entry.name);
}
};
Wenn wir einen neuen Button mit der Klasse .read-dir hinzufügen, öffnet der Aufruf der Methode showDirectoryPicker() beim Klicken den Dateiauswahldialog und listet, wenn ein Verzeichnis auf Ihrem Computer ausgewählt wird, die darin gefundenen Dateien auf.
Dateien löschen
Das Löschen einer Datei in einem Verzeichnis kann mit dem folgenden Code-Snippet erfolgen.
document.querySelector(".pick-file").onclick = async () => {
const [fileHandle] = await window.showOpenFilePicker();
await fileHandle.remove();
};
Wenn Sie einen Ordner löschen möchten, müssen Sie nur eine kleine Änderung am obigen Code-Snippet vornehmen.
document.querySelector(".read-dir").onclick = async () => {
const directoryHandle = await window.showDirectoryPicker();
await directoryHandle.remove();
};
Wenn Sie schließlich eine bestimmte Datei beim Auswählen eines Ordners entfernen möchten, könnten Sie es so schreiben:
// Delete a single file named data.txt in the selected folder
document.querySelector(".pick-folder").onclick = async () => {
const directoryHandle = await window.showDirectoryPicker();
await directoryHandle.removeEntry("data.txt");
};
Und wenn Sie einen gesamten Ordner entfernen möchten, benötigen Sie die folgenden Zeilen:
// Recursively delete the folder named "data"
document.querySelector(".pick-folder").onclick = async () => {
const directoryHandle = await window.showDirectoryPicker();
await directoryHandle.removeEntry('data', { recursive: true });
};
Browser-Unterstützung für die File System Access API
Derzeit scheinen IE und Firefox die File System Access API nicht zu unterstützen. Es gibt jedoch ein Ponyfill namens browser-fs-access.
Diese Daten zur Browserunterstützung stammen von Caniuse, wo weitere Details zu finden sind. Eine Zahl gibt an, dass der Browser die Funktion ab dieser Version unterstützt.
Desktop
| Chrome | Firefox | IE | Edge | Safari |
|---|---|---|---|---|
| 130 | 132 | Nein | 127 | TP |
Mobil / Tablet
| Android Chrome | Android Firefox | Android | iOS Safari |
|---|---|---|---|
| 127 | Nein | 127 | 18.0 |
Zusammenfassung
Wenn Sie die File System Access API ausprobieren möchten, sehen Sie sich diesen Live-Demo-Texteditor an, der von Google-Entwicklern erstellt wurde. Wenn Sie mehr über diese API und ihre Funktionen erfahren möchten, finden Sie hier einige Ressourcen:
- File System Access API (W3C-Spezifikation)
- File System Access API (MDN)
- Contrast Ratio Range, replaceAll Method, Native File System API (Šime Vidas)
- Die File System Access API: Vereinfachter Zugriff auf lokale Dateien (web.dev)
- Dateien und Verzeichnisse mit der Bibliothek browser-fs-access lesen und schreiben (web.dev)
- browser-fs-access Repo (GitHub)
Das ist sehr cool. Ich freue mich darauf, die Weiterentwicklung zu beobachten!
Was gilt als gültige "Benutzerinteraktion"? Ich nehme an, es muss innerhalb eines Event-Handlers für bestimmte Events liegen (gibt es eine Liste?), aber ist es nur das? Muss es sofort im Handler geschehen? Muss es synchron sein?
Obwohl ich diese API mag – ich verwende sie auch in diesem Projekt – gefällt mir nicht, wie instabil sie ist: die Methode
.delete()ist beispielsweise ziemlich neu und wird in vielen Chromium-Forks und abgeleiteten Browsern (wie Ungoogled Chromium) nicht unterstützt. Der am besten unterstützte Weg zum Löschen von Dateien, wenn Sie Browser außer dem offiziellen Chrome und Chromium unterstützen möchten, ist derzeit die Verwendung von.removeEntry().Dasselbe gilt für
.move(): Obwohl es sich um eine sehr nützliche Funktion handelt, wurde sie erst später in die Spezifikation aufgenommen, und die meisten Seiten über diese API erwähnen nicht, dass sie nicht gut unterstützt wird. Außerdem gab es eine Weile.rename(), aber.move()macht jetzt dasselbe und.rename()wurde entfernt.Aufgrund der Instabilität ist es kein Wunder, dass nur Chromium sie implementiert: Abgesehen von potenziellen Sicherheitsproblemen, wie sollen andere Browser mit diesen Änderungen Schritt halten, auch nachdem sie in Chrome/Chromium stabil sind?
Ich lasse Sicherheitsprobleme beiseite, denn wie sollen andere Browser Sicherheit verbessern? Safari hat keine automatische Aktualisierung, Firefox hat in neueren Versionen die Aufforderung zur Bestätigung von Downloads entfernt (die früher aus Sicherheitsgründen sogar eine Verzögerung hatte!).
Für mich werden, obwohl diese API einige Sicherheitsprobleme erleichtert (zum Beispiel gab es *einen* Weg, Benutzer dazu zu bringen, .lnk-Dateien herunterzuladen, die auf Malware verweisen könnten, ohne dass sie wussten, dass diese Dateien gefährlich sind und sie wie Bilddateien aussehen ließen), im Laufe der Zeit diese Probleme gelöst und die Vorteile überwiegen die geringfügigen potenziellen Nachteile.
Andererseits, warum können sie diese API nicht stabilisieren, bevor sie sie veröffentlichen? Warum erstellen sie eine
.rename()-Methode und veröffentlichen sie in Chrome, nur um sie in der nächsten Version zu entfernen? Warum wurde.delete()erst kürzlich hinzugefügt? Warum wiederholen sie dieselben Probleme wie DOM? Sicher, dassel.remove()einfacher ist alsel.parentElement.removeChild(el), dann hätte.delete()früher kommen sollen, anstatt dasselbe mit der File System API zu wiederholen, indem.removeEntry()wie.removeChild()funktioniert. Vielleicht wollten wegen der potenziellen Sicherheitsprobleme nur Chromium-Mitwirkende bei der Entwicklung dieser API helfen und es wurde durcheinander gebracht, ich weiß es nicht. Nun, es ist sogar in der Chromium-Community umstritten: Brave hat es entfernt.Übrigens, weitere Probleme, die ich gefunden habe: Im Gegensatz zu
<a download>kann man damit keine Dateien in einen Download-Ordner herunterladen, ohne den Benutzer aufzufordern; es bereinigt Dateinamen nicht automatisch, sondern wirft einen obskuren Fehler, wenn man einen ungültigen Namen verwendet (und die Verwendung von npm-Bibliotheken funktioniert nicht, da sie – ich habe die meisten davon ausprobiert – nicht alle Zeichen bereinigen, die Chromium als unsicher einstuft, wie aus irgendeinem seltsamen Grund~); der Fehler gibt natürlich nicht an, welche Zeichen nicht erlaubt sind, sodass man raten muss; und schließlich hat es keine Dateisystembeschränkungen bei Dateipfaden, nun, wenn man versucht, eine Datei zu schreiben, die zu einem Dateipfad führt, der größer ist, als das zugrunde liegende Dateisystem akzeptiert, wird dies mit einem obskuren Fehler abgelehnt, der mir, bis ich herausfand, was die Ursache war, einige Kopfschmerzen bereitete.Andererseits hat sie, wie Sie an meinem oben verlinkten Projekt sehen können, ihre eigenen Anwendungsfälle, ist sehr leistungsfähig, nützlich und eine großartige API. Die Tatsache, dass sie das Streamen von Datei-Lese- und Schreibvorgängen unterstützt, ohne einen Service-Worker-Hack zu erfordern, ist großartig: Ohne sie müsste mein Projekt Dateien in den Speicher laden, was das Projekt für die Verarbeitung riesiger Dateien wie Videos unbrauchbar macht. Ich bin sicher, dass sie in Zukunft eine großartige und beliebte API *sein wird*, wenn sie stabiler wird.
Ich denke, diese vereinfachten Code-Snippets sind für die Leser nachteilig. Sie fördern Code, der keine Fehler oder Null-Rückgabewerte prüft. Die Fehlerbehandlung ist ein wichtiger Bestandteil der Verwendung der API. Sie sollte zumindest erwähnt werden.
Wusste nicht, dass man SSL braucht, um Code auszuführen.
Wie funktioniert das mit Paketdateien (z.B. Textbundle-Dateien) oder komprimierten Archiven (z.B. .zip-Archiven)?