Browser können schon lange mit Dateien und Verzeichnissen umgehen. Die File API Funktionen zur Darstellung von Dateiobjekten in Webanwendungen, die programmatische Auswahl und den Zugriff auf ihre Daten. In dem Moment, in dem du genauer hinsiehst, ist jedoch nicht alles Gold, was glänzt.
Die traditionelle Art, mit Dateien zu arbeiten
Dateien werden geöffnet
Als Entwickler können Sie Dateien über die
<input type="file">
-Elements.
In der einfachsten Form kann das Öffnen einer Datei wie im Codebeispiel unten aussehen.
Das input
-Objekt gibt Ihnen ein FileList
,
die im folgenden Fall nur aus einem
File
Ein File
ist eine bestimmte Art von Blob
.
und können in jedem Kontext
verwendet werden, der mit einem Blob möglich ist.
const openFile = async () => {
return new Promise((resolve) => {
const input = document.createElement('input');
input.type = 'file';
input.addEventListener('change', () => {
resolve(input.files[0]);
});
input.click();
});
};
Verzeichnisse werden geöffnet
Für das Öffnen von Ordnern (oder Verzeichnissen) können Sie den
<input webkitdirectory>
.
Ansonsten funktioniert alles wie oben beschrieben.
Trotz des Namens mit Anbieterpräfix
webkitdirectory
kann nicht nur in Chromium- und WebKit-Browsern verwendet werden, sondern auch im alten EdgeHTML-basierten Edge sowie in Firefox.
Speichern (statt Herunterladen) von Dateien
Zum Speichern einer Datei können Sie sie normalerweise nur herunterladen,
Das funktioniert dank des
<a download>
.
Bei einem Blob können Sie das href
-Attribut des Ankers auf eine blob:
-URL setzen, die Sie über die
URL.createObjectURL()
.
const saveFile = async (blob) => {
const a = document.createElement('a');
a.download = 'my-file.txt';
a.href = URL.createObjectURL(blob);
a.addEventListener('click', (e) => {
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
});
a.click();
};
Das Problem
Ein großer Nachteil des Download-Ansatzes ist, dass es keine Möglichkeit gibt, Es gibt keine Möglichkeit, die Originaldatei zu überschreiben. Stattdessen erhalten Sie eine neue Kopie der Originaldatei. im Standardordner "Downloads" des Betriebssystems.
Die File System Access API
Die File System Access API vereinfacht sowohl das Öffnen als auch das Speichern erheblich. Außerdem wird damit das echte Speichern aktiviert, d. h. Sie können nicht nur auswählen, wo eine Datei gespeichert werden soll, aber auch eine vorhandene Datei überschreiben.
Dateien werden geöffnet
Mit der File System Access API
Das Öffnen einer Datei ist nur mit einem Aufruf der window.showOpenFilePicker()
-Methode erforderlich.
Dieser Aufruf gibt ein Datei-Handle zurück, aus dem Sie den tatsächlichen File
über die getFile()
-Methode abrufen können.
const openFile = async () => {
try {
// Always returns an array.
const [handle] = await window.showOpenFilePicker();
return handle.getFile();
} catch (err) {
console.error(err.name, err.message);
}
};
Verzeichnisse werden geöffnet
Öffnen Sie ein Verzeichnis durch Aufrufen von
window.showDirectoryPicker()
, die Verzeichnisse im Dialogfeld „Datei“ auswählbar macht.
Dateien werden gespeichert
Das Speichern von Dateien ist ähnlich einfach.
Über createWritable()
erstellen Sie aus einem Datei-Handle einen beschreibbaren Stream.
Dann schreiben Sie die Blob-Daten, indem Sie die Methode write()
des Streams aufrufen.
Schließlich schließen Sie den Stream durch Aufrufen der close()
-Methode.
const saveFile = async (blob) => {
try {
const handle = await window.showSaveFilePicker({
types: [{
accept: {
// Omitted
},
}],
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return handle;
} catch (err) {
console.error(err.name, err.message);
}
};
Jetzt neu: browser-fs-access
So gut die File System Access API auch ist, sie ist noch nicht allgemein verfügbar.
<ph type="x-smartling-placeholder">Deshalb sehe ich die File System Access API als Progressive Verbesserung. Daher möchte ich sie nutzen, wenn der Browser sie unterstützt. und andernfalls den herkömmlichen Ansatz verwenden. ohne dass Nutzer mit unnötigem Herunterladen von nicht unterstütztem JavaScript-Code bestraft werden. Den Parameter browser-fs-access ist meine Antwort auf diese Herausforderung.
Designphilosophie
Da sich die File System Access API in Zukunft wahrscheinlich noch ändern wird,
die browser-fs-access API nicht danach modelliert wird.
Das heißt, die Bibliothek ist kein Polyfill,
sondern mit einem Ponyfill.
Sie können (statisch oder dynamisch) exklusiv alle Funktionen importieren, die Sie benötigen, um Ihre App so klein wie möglich zu halten.
Die verfügbaren Methoden sind die passend benannten
fileOpen()
,
directoryOpen()
und
fileSave()
Die Bibliotheksfunktion erkennt intern, ob die File System Access API unterstützt wird.
und importiert dann den entsprechenden Codepfad.
Bibliothek des Typs browser-fs-access verwenden
Die drei Methoden sind intuitiv.
Sie können den akzeptierten mimeTypes
oder die Datei extensions
Ihrer Anwendung angeben und ein multiple
-Flag festlegen
, um die Auswahl mehrerer Dateien oder Verzeichnisse zuzulassen oder zu verbieten.
Detaillierte Informationen finden Sie in der
Dokumentation zur API Browser-fs-access
Das folgende Codebeispiel zeigt, wie Sie Bilddateien öffnen und speichern können.
// The imported methods will use the File
// System Access API or a fallback implementation.
import {
fileOpen,
directoryOpen,
fileSave,
} from 'https://unpkg.com/browser-fs-access';
(async () => {
// Open an image file.
const blob = await fileOpen({
mimeTypes: ['image/*'],
});
// Open multiple image files.
const blobs = await fileOpen({
mimeTypes: ['image/*'],
multiple: true,
});
// Open all files in a directory,
// recursively including subdirectories.
const blobsInDirectory = await directoryOpen({
recursive: true
});
// Save a file.
await fileSave(blob, {
fileName: 'Untitled.png',
});
})();
Demo
Du kannst den obigen Code in einer Demo zu Glitch sehen. Der Quellcode ist ebenfalls dort verfügbar. Da aus Sicherheitsgründen ursprungsübergreifende Subframes keine Dateiauswahl anzeigen dürfen, Die Demo kann nicht in diesen Artikel eingebettet werden.
Die Bibliothek „browser-fs-access“ in der Praxis
In meiner Freizeit trage ich einen kleinen Beitrag zu installierbare PWA namens Excalidraw, ein Whiteboard-Tool, mit dem Sie ganz einfach von Hand gezeichnete Diagramme skizzieren können. Sie ist vollständig responsiv und funktioniert auf zahlreichen Geräten gut – von kleinen Smartphones bis hin zu Computern mit großen Bildschirmen. Das bedeutet, dass es Dateien auf allen verschiedenen Plattformen verarbeiten muss, ob sie die File System Access API unterstützen. Daher eignet sie sich hervorragend für die Bibliothek browser-fs-access.
Ich kann zum Beispiel eine Zeichnung auf meinem iPhone erstellen, Speichern (technisch: Download, da Safari die File System Access API nicht unterstützt) im Ordner "Downloads" auf dem iPhone, öffne die Datei auf meinem Desktop (nach der Übertragung von meinem Smartphone), die Datei ändern und sie mit meinen Änderungen überschreiben oder sie sogar als neue Datei speichern.
<ph type="x-smartling-placeholder"> <ph type="x-smartling-placeholder"> <ph type="x-smartling-placeholder"> <ph type="x-smartling-placeholder">Codebeispiel aus der Praxis
Unten sehen Sie ein Beispiel für die Verwendung von „browser-fs-access“ in Excalidraw.
Dieser Auszug stammt aus
/src/data/json.ts
Von besonderem Interesse ist, wie die saveAsJSON()
-Methode entweder ein Datei-Handle oder null
an browser-fs-access übergibt.
fileSave()
-Methode, mit der es überschrieben wird, wenn ein Handle angegeben wird,
oder in einer neuen Datei speichern.
export const saveAsJSON = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
fileHandle: any,
) => {
const serialized = serializeAsJSON(elements, appState);
const blob = new Blob([serialized], {
type: "application/json",
});
const name = `${appState.name}.excalidraw`;
(window as any).handle = await fileSave(
blob,
{
fileName: name,
description: "Excalidraw file",
extensions: ["excalidraw"],
},
fileHandle || null,
);
};
export const loadFromJSON = async () => {
const blob = await fileOpen({
description: "Excalidraw files",
extensions: ["json", "excalidraw"],
mimeTypes: ["application/json"],
});
return loadFromBlob(blob);
};
Hinweise zur Benutzeroberfläche
Ob in Excalidraw oder in Ihrer App,
sollte sich die Benutzeroberfläche
an die Support-Situation des Browsers anpassen.
Wenn die File System Access API unterstützt wird (if ('showOpenFilePicker' in window) {}
)
können Sie neben Speichern auch die Schaltfläche Speichern unter verwenden.
Die folgenden Screenshots zeigen den Unterschied zwischen der responsiven Haupt-App-Symbolleiste von Excalidraw auf einem iPhone und in der Chrome-Desktop-App.
Beachte, dass auf dem iPhone die Schaltfläche Speichern unter fehlt.
Ergebnisse
Die Arbeit mit Systemdateien funktioniert technisch in allen modernen Browsern. Für Browser, die die File System Access API unterstützen, können Sie die Nutzung optimieren, indem Sie zum echten Speichern und Überschreiben (nicht nur für das Herunterladen) von Dateien und indem Sie Ihren Nutzern die Möglichkeit geben, an jedem beliebigen Ort neue Dateien zu erstellen, in Browsern, die die File System Access API nicht unterstützen, weiterhin funktionieren. browser-fs-access erleichtert Ihnen das Leben indem Sie mit den Feinheiten Progressive Enhancement umzugehen und Ihren Code so einfach wie möglich zu gestalten.
Danksagungen
Dieser Artikel wurde von Joe Medley geprüft und Kayce Basques Vielen Dank an die Mitwirkenden an Excalidraw für ihre Arbeit am Projekt und für die Überprüfung meiner Pull-Anfragen. Hero-Image von Ilya Pawlow auf Unsplash.