Die File System Access API: vereinfachter Zugriff auf lokale Dateien

Mit der File System Access API können Web-Apps Änderungen an Dateien und Ordnern auf dem Gerät des Nutzers lesen oder direkt speichern.

Was ist die File System Access API?

Mit der File System Access API (früher Native File System API, früher als „Writeable Files API“ bezeichnet) können Entwickler leistungsstarke Webanwendungen erstellen, die mit Dateien auf dem lokalen Gerät des Nutzers interagieren, z. B. IDEs, Foto- und Videoeditoren und Texteditoren. Nachdem ein Nutzer einer Web-App Zugriff gewährt hat, kann er mit dieser API Änderungen direkt an Dateien und Ordnern auf dem Gerät des Nutzers lesen oder speichern. Neben dem Lesen und Schreiben von Dateien bietet die File System Access API auch die Möglichkeit, ein Verzeichnis zu öffnen und seinen Inhalt aufzulisten.

Wenn Sie bereits Dateien lesen und schreiben, wird Ihnen vieles von dem, was ich hier vorstellen werde, vertraut sein. Ich empfehle dir, es trotzdem zu lesen, denn nicht alle Systeme sind gleich.

Die File System Access API wird derzeit in den meisten Chromium-Browsern unter Windows, macOS, ChromeOS und Linux unterstützt. Eine beachtenswerte Ausnahme ist Brave. Dort ist es derzeit nur hinter einem Flag verfügbar. Seit Chromium 109 unterstützt Android den Teil des privaten Dateisystems der API. Es gibt derzeit keine Pläne für Auswahlmethoden, aber Sie können den potenziellen Fortschritt verfolgen, indem Sie crbug.com/1011535 markieren.

File System Access API verwenden

Ich habe einen Texteditor für eine einzelne Datei entwickelt, um die Leistungsfähigkeit und Nützlichkeit der File System Access API zu demonstrieren. Sie können damit eine Textdatei öffnen, bearbeiten, die Änderungen wieder auf der Festplatte speichern oder eine neue Datei starten und die Änderungen auf der Festplatte speichern. Sie ist nicht aufwendig, aber ausreichend, um Ihnen das Verständnis der Konzepte zu erleichtern.

Unterstützte Browser

Unterstützte Browser

  • 86
  • 86
  • x
  • x

Quelle

Jetzt testen

Sehen Sie sich in der Demo des Texteditors die File System Access API in Aktion an.

Datei aus dem lokalen Dateisystem lesen

Der erste Anwendungsfall, den ich behandeln möchte, besteht darin, den Nutzer zu bitten, eine Datei auszuwählen, diese Datei dann zu öffnen und von der Festplatte zu lesen.

Nutzer bitten, eine Datei zum Lesen auszuwählen

Der Einstiegspunkt für die File System Access API ist window.showOpenFilePicker(). Beim Aufruf wird ein Dialogfeld für die Dateiauswahl angezeigt und der Nutzer wird aufgefordert, eine Datei auszuwählen. Nachdem sie eine Datei ausgewählt haben, gibt die API ein Array von Datei-Handles zurück. Mit dem optionalen options-Parameter können Sie das Verhalten der Dateiauswahl beeinflussen, z. B. indem Sie zulassen, dass Nutzer mehrere Dateien, Verzeichnisse oder verschiedene Dateitypen auswählen können. Wenn keine Optionen angegeben sind, kann der Nutzer über die Dateiauswahl eine einzelne Datei auswählen. Diese ist ideal für einen Texteditor.

Wie viele andere leistungsstarke APIs muss showOpenFilePicker() in einem sicheren Kontext und aus einer Nutzergeste heraus aufgerufen werden.

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  // Destructure the one-element array.
  [fileHandle] = await window.showOpenFilePicker();
  // Do something with the file handle.
});

Sobald der Nutzer eine Datei auswählt, gibt showOpenFilePicker() ein Array mit Handles zurück. In diesem Fall ist das ein Ein-Element-Array mit einem FileSystemFileHandle, das die Attribute und Methoden enthält, die für die Interaktion mit der Datei erforderlich sind.

Es ist hilfreich, einen Verweis auf den Datei-Handle aufzubewahren, damit er später verwendet werden kann. Sie ist erforderlich, um Änderungen in der Datei zu speichern oder andere Dateivorgänge auszuführen.

Dateien aus dem Dateisystem lesen

Da Sie nun über einen Handle für eine Datei verfügen, können Sie die Eigenschaften der Datei abrufen oder auf die Datei selbst zugreifen. Ich lese vorerst einfach den Inhalt vor. Der Aufruf von handle.getFile() gibt ein File-Objekt zurück, das ein Blob enthält. Rufen Sie eine der zugehörigen Methoden (slice(), stream(), text() oder arrayBuffer()) auf, um die Daten aus dem Blob abzurufen.

const file = await fileHandle.getFile();
const contents = await file.text();

Das von FileSystemFileHandle.getFile() zurückgegebene File-Objekt ist nur lesbar, solange die zugrunde liegende Datei auf dem Laufwerk nicht geändert wurde. Wenn die Datei auf dem Laufwerk geändert wird, kann das File-Objekt nicht mehr gelesen werden und Sie müssen getFile() noch einmal aufrufen, damit ein neues File-Objekt die geänderten Daten liest.

Zusammenfassung

Wenn Nutzer auf die Schaltfläche „Öffnen“ klicken, wird im Browser eine Dateiauswahl angezeigt. Nachdem sie eine Datei ausgewählt haben, liest die App den Inhalt und fügt ihn in eine <textarea> ein.

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  [fileHandle] = await window.showOpenFilePicker();
  const file = await fileHandle.getFile();
  const contents = await file.text();
  textArea.value = contents;
});

Datei in das lokale Dateisystem schreiben

Im Texteditor haben Sie zwei Möglichkeiten, eine Datei zu speichern: Speichern und Speichern unter. Mit Speichern werden die Änderungen einfach mithilfe des zuvor abgerufenen Datei-Handles in die Originaldatei zurückgeschrieben. Mit Speichern unter wird jedoch eine neue Datei erstellt und daher ein neues Datei-Handle erforderlich.

Neue Datei erstellen

Rufen Sie zum Speichern einer Datei showSaveFilePicker() auf. Dadurch wird die Dateiauswahl im „Speichern“-Modus angezeigt, sodass der Nutzer eine neue Datei zum Speichern auswählen kann. Für den Texteditor wollte ich außerdem automatisch die Erweiterung .txt hinzufügen. Deshalb habe ich einige zusätzliche Parameter angegeben.

async function getNewFileHandle() {
  const options = {
    types: [
      {
        description: 'Text Files',
        accept: {
          'text/plain': ['.txt'],
        },
      },
    ],
  };
  const handle = await window.showSaveFilePicker(options);
  return handle;
}

Änderungen auf Laufwerk speichern

Den gesamten Code zum Speichern von Änderungen an einer Datei finden Sie in meiner Texteditor-Demo auf GitHub. Die zentralen Interaktionen des Dateisystems finden Sie in fs-helpers.js. Im einfachsten Fall sieht der Vorgang wie der folgende Code aus. Ich gehe die einzelnen Schritte durch und erkläre es.

// fileHandle is an instance of FileSystemFileHandle..
async function writeFile(fileHandle, contents) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Write the contents of the file to the stream.
  await writable.write(contents);
  // Close the file and write the contents to disk.
  await writable.close();
}

Beim Schreiben von Daten auf das Laufwerk wird ein FileSystemWritableFileStream-Objekt verwendet, eine abgeleitete Klasse von WritableStream. Erstellen Sie den Stream, indem Sie createWritable() für das Datei-Handle-Objekt aufrufen. Wenn createWritable() aufgerufen wird, prüft der Browser zuerst, ob der Nutzer eine Schreibberechtigung für die Datei erteilt hat. Wenn die Berechtigung zum Schreiben nicht gewährt wurde, fordert der Browser den Nutzer auf, die Berechtigung zum Schreiben zu erteilen. Wenn die Berechtigung nicht gewährt wird, gibt createWritable() ein DOMException aus und die App kann nicht in die Datei schreiben. Im Texteditor werden die DOMException-Objekte in der Methode saveFile() verarbeitet.

Für die Methode write() wird ein String verwendet, der für einen Texteditor erforderlich ist. Sie kann aber auch eine BufferSource oder ein Blob verwenden. Sie können beispielsweise einen Stream direkt an diesen senden:

async function writeURLToFile(fileHandle, url) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Make an HTTP request for the contents.
  const response = await fetch(url);
  // Stream the response into the file.
  await response.body.pipeTo(writable);
  // pipeTo() closes the destination pipe by default, no need to close it.
}

Sie können im Stream auch seek() oder truncate() verwenden, um die Datei an einer bestimmten Position zu aktualisieren oder die Größe der Datei zu ändern.

Vorgeschlagenen Dateinamen und Startverzeichnis angeben

In vielen Fällen möchten Sie vielleicht, dass Ihre App einen Standarddateinamen oder -speicherort vorschlägt. Beispielsweise kann ein Texteditor den Standarddateinamen Untitled Text.txt anstelle von Untitled vorschlagen. Dazu können Sie das Attribut suggestedName als Teil der showSaveFilePicker-Optionen übergeben.

const fileHandle = await self.showSaveFilePicker({
  suggestedName: 'Untitled Text.txt',
  types: [{
    description: 'Text documents',
    accept: {
      'text/plain': ['.txt'],
    },
  }],
});

Dasselbe gilt für das Standardstartverzeichnis. Wenn Sie einen Texteditor erstellen, können Sie das Dialogfeld zum Speichern oder Öffnen von Dateien im Standardordner documents starten, bei einem Bildeditor hingegen im Standardordner pictures. Sie können ein Standardstartverzeichnis vorschlagen, indem Sie das Attribut startIn an die Methoden showSaveFilePicker, showDirectoryPicker() oder showOpenFilePicker übergeben.

const fileHandle = await self.showOpenFilePicker({
  startIn: 'pictures'
});

Die Liste der bekannten Systemverzeichnisse lautet:

  • desktop: Desktopverzeichnis des Nutzers, sofern vorhanden.
  • documents: Verzeichnis, in dem vom Nutzer erstellte Dokumente normalerweise gespeichert werden.
  • downloads: Verzeichnis, in dem heruntergeladene Dateien normalerweise gespeichert werden.
  • music: Verzeichnis, in dem Audiodateien normalerweise gespeichert werden.
  • pictures: Verzeichnis, in dem normalerweise Fotos und andere Standbilder gespeichert werden
  • videos: Verzeichnis, in dem normalerweise Videos/Filme gespeichert werden.

Neben bekannten Systemverzeichnissen können Sie auch einen vorhandenen Datei- oder Verzeichnis-Handle als Wert für startIn übergeben. Das Dialogfeld wird dann im selben Verzeichnis geöffnet.

// Assume `directoryHandle` is a handle to a previously opened directory.
const fileHandle = await self.showOpenFilePicker({
  startIn: directoryHandle
});

Zweck verschiedener Dateiauswahlen angeben

Manchmal haben Anwendungen unterschiedliche Auswahlmöglichkeiten für unterschiedliche Zwecke. Mit einem Rich-Text-Editor können Nutzer beispielsweise Textdateien öffnen, aber auch Bilder importieren. Standardmäßig wird jede Dateiauswahl am zuletzt gespeicherten Speicherort geöffnet. Sie können dies umgehen, indem Sie id-Werte für jeden Auswahltyp speichern. Wenn ein id angegeben ist, merkt sich die Dateiauswahl ein separates, zuletzt verwendetes Verzeichnis für diese id.

const fileHandle1 = await self.showSaveFilePicker({
  id: 'openText',
});

const fileHandle2 = await self.showSaveFilePicker({
  id: 'importImage',
});

Datei-Handles oder Verzeichnis-Handles in IndexedDB werden gespeichert

Datei-Handles und Verzeichnis-Handles sind seriell, d. h. Sie können eine Datei oder ein Verzeichnis-Handle in IndexedDB speichern oder postMessage() aufrufen, um sie zwischen demselben Ursprung der obersten Ebene zu senden.

Wenn Sie Datei- oder Verzeichnis-Handles in IndexedDB speichern, können Sie den Status speichern oder sich merken, an welchen Dateien oder Verzeichnissen ein Nutzer gearbeitet hat. Auf diese Weise können Sie eine Liste der zuletzt geöffneten oder bearbeiteten Dateien beibehalten, anbieten, die letzte Datei beim Öffnen der Anwendung noch einmal zu öffnen, das vorherige Arbeitsverzeichnis wiederherstellen und vieles mehr. Im Texteditor speichere ich eine Liste der fünf zuletzt vom Nutzer geöffneten Dateien, damit ich leicht wieder auf diese Dateien zugreifen kann.

Das folgende Codebeispiel zeigt, wie ein Datei-Handle und ein Verzeichnis-Handle gespeichert und abgerufen werden. Das kannst du dir auf Glitch in Aktion ansehen. Der Einfachheit halber verwende ich die Bibliothek idb-keyval.

import { get, set } from 'https://unpkg.com/idb-keyval@5.0.2/dist/esm/index.js';

const pre1 = document.querySelector('pre.file');
const pre2 = document.querySelector('pre.directory');
const button1 = document.querySelector('button.file');
const button2 = document.querySelector('button.directory');

// File handle
button1.addEventListener('click', async () => {
  try {
    const fileHandleOrUndefined = await get('file');
    if (fileHandleOrUndefined) {
      pre1.textContent = `Retrieved file handle "${fileHandleOrUndefined.name}" from IndexedDB.`;
      return;
    }
    const [fileHandle] = await window.showOpenFilePicker();
    await set('file', fileHandle);
    pre1.textContent = `Stored file handle for "${fileHandle.name}" in IndexedDB.`;
  } catch (error) {
    alert(error.name, error.message);
  }
});

// Directory handle
button2.addEventListener('click', async () => {
  try {
    const directoryHandleOrUndefined = await get('directory');
    if (directoryHandleOrUndefined) {
      pre2.textContent = `Retrieved directroy handle "${directoryHandleOrUndefined.name}" from IndexedDB.`;
      return;
    }
    const directoryHandle = await window.showDirectoryPicker();
    await set('directory', directoryHandle);
    pre2.textContent = `Stored directory handle for "${directoryHandle.name}" in IndexedDB.`;
  } catch (error) {
    alert(error.name, error.message);
  }
});

Handles und Berechtigungen für gespeicherte Dateien oder Verzeichnisse

Da Berechtigungen zwischen Sitzungen derzeit nicht beibehalten werden, sollten Sie prüfen, ob der Nutzer mit queryPermission() die Berechtigung für die Datei oder das Verzeichnis gewährt hat. Ist dies nicht der Fall, rufen Sie requestPermission() auf, um sie (noch einmal) anzufordern. Das funktioniert für Datei- und Verzeichnis-Handles gleich. Sie müssen fileOrDirectoryHandle.requestPermission(descriptor) bzw. fileOrDirectoryHandle.queryPermission(descriptor) ausführen.

Im Texteditor habe ich eine verifyPermission()-Methode erstellt, die prüft, ob der Nutzer bereits eine Berechtigung erteilt hat, und die Anfrage bei Bedarf stellt.

async function verifyPermission(fileHandle, readWrite) {
  const options = {};
  if (readWrite) {
    options.mode = 'readwrite';
  }
  // Check if permission was already granted. If so, return true.
  if ((await fileHandle.queryPermission(options)) === 'granted') {
    return true;
  }
  // Request permission. If the user grants permission, return true.
  if ((await fileHandle.requestPermission(options)) === 'granted') {
    return true;
  }
  // The user didn't grant permission, so return false.
  return false;
}

Indem ich die Schreibberechtigung mit der Leseanfrage anforderte, reduzierte ich die Anzahl der Berechtigungsaufforderungen. Der Nutzer sieht beim Öffnen der Datei eine Aufforderung und erteilt Lese- und Schreibzugriff.

Verzeichnis öffnen und seinen Inhalt auflisten

Rufen Sie showDirectoryPicker() auf, um alle Dateien in einem Verzeichnis aufzulisten. Der Nutzer wählt in einer Auswahl ein Verzeichnis aus. Anschließend wird ein FileSystemDirectoryHandle zurückgegeben, mit dem Sie die Dateien des Verzeichnisses auflisten und darauf zugreifen können. Standardmäßig haben Sie Lesezugriff auf die Dateien im Verzeichnis. Wenn Sie jedoch Schreibzugriff benötigen, können Sie { mode: 'readwrite' } an die Methode übergeben.

const butDir = document.getElementById('butDirectory');
butDir.addEventListener('click', async () => {
  const dirHandle = await window.showDirectoryPicker();
  for await (const entry of dirHandle.values()) {
    console.log(entry.kind, entry.name);
  }
});

Wenn Sie zusätzlich über getFile() auf jede Datei zugreifen müssen, um beispielsweise die einzelnen Dateigrößen zu erhalten, verwenden Sie await nicht nacheinander für jedes Ergebnis, sondern verarbeiten Sie alle Dateien parallel, z. B. über Promise.all().

const butDir = document.getElementById('butDirectory');
butDir.addEventListener('click', async () => {
  const dirHandle = await window.showDirectoryPicker();
  const promises = [];
  for await (const entry of dirHandle.values()) {
    if (entry.kind !== 'file') {
      continue;
    }
    promises.push(entry.getFile().then((file) => `${file.name} (${file.size})`));
  }
  console.log(await Promise.all(promises));
});

Dateien und Ordner in einem Verzeichnis erstellen oder darauf zugreifen

Aus einem Verzeichnis können Sie mit der Methode getFileHandle() bzw. getDirectoryHandle() Dateien und Ordner erstellen oder darauf zugreifen. Wenn Sie ein optionales options-Objekt mit dem Schlüssel create und dem booleschen Wert true oder false übergeben, können Sie festlegen, ob eine neue Datei oder ein neuer Ordner erstellt werden soll, sofern noch keine vorhanden ist.

// In an existing directory, create a new directory named "My Documents".
const newDirectoryHandle = await existingDirectoryHandle.getDirectoryHandle('My Documents', {
  create: true,
});
// In this new directory, create a file named "My Notes.txt".
const newFileHandle = await newDirectoryHandle.getFileHandle('My Notes.txt', { create: true });

Pfad eines Elements in einem Verzeichnis auflösen

Wenn Sie mit Dateien oder Ordnern in einem Verzeichnis arbeiten, kann es hilfreich sein, den Pfad des betreffenden Elements aufzulösen. Dazu kann die passend benannte Methode resolve() verwendet werden. Zur Auflösung kann das Element dem Verzeichnis direkt oder indirekt untergeordnet sein.

// Resolve the path of the previously created file called "My Notes.txt".
const path = await newDirectoryHandle.resolve(newFileHandle);
// `path` is now ["My Documents", "My Notes.txt"]

Dateien und Ordner in einem Verzeichnis löschen

Wenn Sie Zugriff auf ein Verzeichnis haben, können Sie die darin enthaltenen Dateien und Ordner mit der Methode removeEntry() löschen. Bei Ordnern kann das Löschen optional rekursiv sein und alle Unterordner und die darin enthaltenen Dateien umfassen.

// Delete a file.
await directoryHandle.removeEntry('Abandoned Projects.txt');
// Recursively delete a folder.
await directoryHandle.removeEntry('Old Stuff', { recursive: true });

Dateien oder Ordner direkt löschen

Wenn Sie Zugriff auf eine Datei- oder Verzeichnis-Handle haben, rufen Sie remove() für FileSystemFileHandle oder FileSystemDirectoryHandle auf, um sie zu entfernen.

// Delete a file.
await fileHandle.remove();
// Delete a directory.
await directoryHandle.remove();

Dateien und Ordner umbenennen und verschieben

Dateien und Ordner können durch Aufrufen von move() auf der FileSystemHandle-Oberfläche umbenannt oder an einen neuen Speicherort verschoben werden. FileSystemHandle hat die untergeordneten Schnittstellen FileSystemFileHandle und FileSystemDirectoryHandle. Die Methode move() verwendet einen oder zwei Parameter. Die erste kann entweder ein String mit dem neuen Namen oder ein FileSystemDirectoryHandle für den Zielordner sein. In letzterem Fall ist der optionale zweite Parameter ein String mit dem neuen Namen, sodass das Verschieben und Umbenennen in einem Schritt erfolgen kann.

// Rename the file.
await file.move('new_name');
// Move the file to a new directory.
await file.move(directory);
// Move the file to a new directory and rename it.
await file.move(directory, 'newer_name');

Drag-and-drop-Integration

Mithilfe der HTML Drag-and-drop-Oberflächen können Webanwendungen auf einer Webseite gezogene und gezogene Dateien akzeptieren. Während eines Drag-and-drop-Vorgangs werden per Drag-and-drop eingefügte Datei- und Verzeichniselemente mit Datei- bzw. Verzeichniseinträgen verknüpft. Die Methode DataTransferItem.getAsFileSystemHandle() gibt ein Promise mit einem FileSystemFileHandle-Objekt zurück, wenn das gezogene Element eine Datei ist, und ein Promise mit einem FileSystemDirectoryHandle-Objekt, wenn das gezogene Element ein Verzeichnis ist. Der folgende Eintrag zeigt dies in Aktion. Beachten Sie, dass der DataTransferItem.kind der Drag-and-drop-Oberfläche sowohl für Dateien als auch für Verzeichnisse "file" ist, während FileSystemHandle.kind der File System Access API "file" für Dateien und "directory" für Verzeichnisse ist.

elem.addEventListener('dragover', (e) => {
  // Prevent navigation.
  e.preventDefault();
});

elem.addEventListener('drop', async (e) => {
  e.preventDefault();

  const fileHandlesPromises = [...e.dataTransfer.items]
    .filter((item) => item.kind === 'file')
    .map((item) => item.getAsFileSystemHandle());

  for await (const handle of fileHandlesPromises) {
    if (handle.kind === 'directory') {
      console.log(`Directory: ${handle.name}`);
    } else {
      console.log(`File: ${handle.name}`);
    }
  }
});

Auf das private Dateisystem des Ursprungs zugreifen

Das private Dateisystem des Ursprungs ist ein Speicherendpunkt, der, wie der Name schon sagt, privat für den Ursprung der Seite ist. Browser implementieren dies in der Regel, indem sie den Inhalt dieses privaten Ursprungsdateisystems irgendwo auf einem Laufwerk speichern. Es ist jedoch nicht vorgesehen, dass der Inhalt für Nutzer leicht zugänglich ist. Außerdem wird nicht davon ausgegangen, dass Dateien oder Verzeichnisse mit Namen vorhanden sind, die den Namen untergeordneter Elemente des privaten Ursprungsdateisystems entsprechen. Während der Browser den Anschein erwecken könnte, dass es Dateien gibt, speichert der Browser diese „Dateien“ intern in einer Datenbank oder einer anderen Datenstruktur, da dies ein privates Dateisystem ist. Wenn Sie diese API verwenden, erwarten Sie nicht, dass die erstellten Dateien irgendwo auf der Festplatte genau übereinstimmen. Sobald Sie Zugriff auf das Stammverzeichnis FileSystemDirectoryHandle haben, können Sie wie gewohnt mit dem privaten Dateisystem des Ursprungs arbeiten.

const root = await navigator.storage.getDirectory();
// Create a new file handle.
const fileHandle = await root.getFileHandle('Untitled.txt', { create: true });
// Create a new directory handle.
const dirHandle = await root.getDirectoryHandle('New Folder', { create: true });
// Recursively remove a directory.
await root.removeEntry('Old Stuff', { recursive: true });

Unterstützte Browser

  • 86
  • 86
  • 111
  • 15.2

Quelle

Über das private Ursprungsdateisystem auf Dateien zugreifen, die für die Leistung optimiert sind

Das private Ursprungsdateisystem bietet optionalen Zugriff auf eine spezielle Art von Datei, die hochgradig leistungsoptimiert ist, indem es beispielsweise direkten und exklusiven Schreibzugriff auf den Inhalt einer Datei bietet. In Chromium 102 und höher gibt es im privaten Dateisystem eine zusätzliche Methode zur Vereinfachung des Dateizugriffs: createSyncAccessHandle() (für synchrone Lese- und Schreibvorgänge). Es wird unter FileSystemFileHandle bereitgestellt, aber ausschließlich in Web Workers.

// (Read and write operations are synchronous,
// but obtaining the handle is asynchronous.)
// Synchronous access exclusively in Worker contexts.
const accessHandle = await fileHandle.createSyncAccessHandle();
const writtenBytes = accessHandle.write(buffer);
const readBytes = accessHandle.read(buffer, { at: 1 });

Polyfilling

Es ist nicht möglich, die Methoden der File System Access API vollständig mit Polyfill zu füllen.

  • Die Methode showOpenFilePicker() kann mit einem <input type="file">-Element annähernd werden.
  • Die Methode showSaveFilePicker() kann mit einem <a download="file_name">-Element simuliert werden. Dies löst jedoch einen programmatischen Download aus und ermöglicht das Überschreiben vorhandener Dateien nicht.
  • Die Methode showDirectoryPicker() kann mit dem nicht standardmäßigen <input type="file" webkitdirectory>-Element emuliert werden.

Wir haben eine Bibliothek mit dem Namen browser-fs-access entwickelt, die nach Möglichkeit die File System Access API verwendet und in allen anderen Fällen auf diese nächstbeste Option zurückgreift.

Sicherheit und Berechtigungen

Das Chrome-Team hat die File System Access API gemäß den unter Zugriff auf leistungsstarke Webplattform-Funktionen steuern definierten Grundprinzipien wie Nutzersteuerung, Transparenz und Ergonomie entwickelt.

Eine Datei öffnen oder eine neue Datei speichern

Dateiauswahl zum Öffnen einer Datei zum Lesen
Eine Dateiauswahl, mit der eine vorhandene Datei zum Lesen geöffnet wird.

Beim Öffnen einer Datei erteilt der Nutzer die Berechtigung, eine Datei oder ein Verzeichnis über die Dateiauswahl zu lesen. Die Auswahl für geöffnete Datei kann nur über eine Nutzergeste angezeigt werden, wenn sie aus einem sicheren Kontext bereitgestellt wird. Wenn Nutzer ihre Meinung ändern, können sie die Auswahl in der Dateiauswahl abbrechen und die Website erhält keinen Zugriff mehr. Dies entspricht dem Verhalten des Elements <input type="file">.

Dateiauswahl zum Speichern einer Datei auf dem Laufwerk.
Eine Dateiauswahl zum Speichern einer Datei auf dem Laufwerk.

Wenn eine Webanwendung eine neue Datei speichern möchte, zeigt der Browser die Auswahl zum Speichern der Datei an, mit der der Nutzer den Namen und den Speicherort der neuen Datei angeben kann. Da sie eine neue Datei auf dem Gerät speichern (im Gegensatz zum Überschreiben einer vorhandenen Datei), gewährt die Dateiauswahl der App die Berechtigung, in die Datei zu schreiben.

Eingeschränkte Ordner

Um Nutzer und ihre Daten zu schützen, kann der Browser die Fähigkeit des Nutzers einschränken, Dateien in bestimmten Ordnern zu speichern, z. B. in den Hauptordnern des Betriebssystems wie Windows oder den macOS-Bibliotheksordnern. In diesem Fall zeigt der Browser eine Aufforderung an und fordert den Nutzer auf, einen anderen Ordner auszuwählen.

Vorhandene Datei oder Verzeichnis ändern

Eine Webanwendung kann eine Datei auf dem Laufwerk nur mit ausdrücklicher Berechtigung des Nutzers ändern.

Berechtigungsaufforderung

Wenn eine Person Änderungen an einer Datei speichern möchte, auf die sie zuvor Lesezugriff gewährt hat, zeigt der Browser eine Berechtigungsaufforderung an, in der die Website um die Berechtigung gebeten wird, Änderungen auf das Laufwerk zu schreiben. Die Berechtigungsanfrage kann nur durch eine Nutzergeste ausgelöst werden, z. B. durch Klicken auf die Schaltfläche „Speichern“.

Berechtigungsaufforderung, die vor dem Speichern einer Datei angezeigt wird.
Aufforderung, die Nutzern angezeigt wird, bevor dem Browser die Schreibberechtigung für eine vorhandene Datei gewährt wird.

Alternativ kann eine Webanwendung, die mehrere Dateien bearbeitet, wie z. B. eine IDE auch die Berechtigung zum Speichern von Änderungen beim Öffnen anfordern.

Wenn der Nutzer „Abbrechen“ auswählt und keinen Schreibzugriff gewährt, kann die Web-App keine Änderungen an der lokalen Datei speichern. Er sollte dem Nutzer eine alternative Methode zum Speichern seiner Daten bieten, z. B. indem er eine Möglichkeit bietet, die Datei herunterzuladen oder Daten in der Cloud zu speichern.

Transparenz

Omnibox-Symbol
Omnibox-Symbol, das angibt, dass der Nutzer der Website die Berechtigung zum Speichern in einer lokalen Datei erteilt hat.

Sobald ein Nutzer einer Webanwendung die Berechtigung zum Speichern einer lokalen Datei erteilt hat, zeigt der Browser ein Symbol in der URL-Leiste an. Wenn Sie auf das Symbol klicken, wird ein Pop-over mit der Liste der Dateien geöffnet, auf die der Nutzer Zugriff gewährt hat. Der Nutzer kann diesen Zugriff nach Belieben widerrufen.

Berechtigungspersistenz

Die Web-App kann Änderungen an der Datei weiterhin ohne Nachfrage speichern, bis alle Tabs für den Ursprung geschlossen sind. Sobald ein Tab geschlossen wird, hat die Website keinen Zugriff mehr. Wenn der Nutzer das nächste Mal die Webanwendung verwendet, wird er zum Zugriff auf die Dateien aufgefordert.

Feedback

Wir würden gern mehr über Ihre Erfahrungen mit der File System Access API hören.

Informationen zum API-Design

Gibt es etwas an der API, das nicht so funktioniert, wie Sie es erwartet hatten? Oder fehlen Methoden oder Eigenschaften, um Ihre Idee zu implementieren? Haben Sie eine Frage oder einen Kommentar zum Sicherheitsmodell?

Probleme bei der Implementierung?

Haben Sie einen Fehler bei der Implementierung in Chrome gefunden? Oder unterscheidet sich die Implementierung von der Spezifikation?

  • Melden Sie einen Fehler unter https://new.crbug.com. Geben Sie so viele Details wie möglich und eine einfache Anleitung zum Reproduzieren an. Setzen Sie Components (Komponenten) auf Blink>Storage>FileSystem. Glitch eignet sich perfekt, um schnelle und einfache Reproduzierungen zu teilen.

Möchten Sie die API verwenden?

Möchten Sie die File System Access API auf Ihrer Website verwenden? Ihre öffentliche Unterstützung hilft uns dabei, Funktionen zu priorisieren und zeigt anderen Browseranbietern, wie wichtig es ist, sie zu unterstützen.

Nützliche Links

Danksagungen

Die Spezifikation für die File System Access API wurde von Marijn Kruisselbrink geschrieben.