Start · Sprachen · JavaScript · Referenz · DataTransferItem.getAsFileSystemHandle()

DataTransferItem.getAsFileSystemHandle()

Methode

Liefert ein <code>Promise</code>, das bei gezogenen Dateien mit einem <code>FileSystemFileHandle</code> und bei Ordnern mit einem <code>FileSystemDirectoryHandle</code> erfüllt wird.

seit JavaScript Chromium 86+ (Chrome, Edge, Opera); nicht in Firef Kategorie: webapi

Signatur

DataTransferItem.prototype.getAsFileSystemHandle()

Beschreibung

Mit getAsFileSystemHandle() verbindet man Drag & Drop mit der File System Access API. Zieht der Nutzer eine Datei oder einen Ordner auf die Seite, erhält man statt eines einfachen File-Objekts ein Handle, das dauerhaft auf den Eintrag im Dateisystem zeigt. Über dieses Handle kann man später erneut lesen, mit createWritable() zurückschreiben oder, bei Ordnern, den Inhalt rekursiv durchlaufen.

Die Methode ist nur für Einträge mit kind === "file" sinnvoll. Bei Text-Einträgen (kind === "string") wird das Promise mit null erfüllt. Welches Handle man bekommen hat, zeigt die Eigenschaft handle.kind: Sie ist entweder "file" oder "directory".

Wichtig: Die DataTransferItemList gibt es nur, solange das drop-Ereignis synchron läuft. Deshalb muss man alle Aufrufe von getAsFileSystemHandle() sofort im Handler starten und die Promises sammeln. Erst danach darf man mit await auf sie warten. Wer vor einem weiteren Aufruf ein await setzt, bekommt für die restlichen Einträge keine gültigen Daten mehr.

Die Methode eignet sich besonders für Editoren, Bildbearbeitung oder IDE-ähnliche Web-Apps. Dort sollen gezogene Dateien direkt bearbeitet und gespeichert werden, ohne dass sie neu heruntergeladen werden müssen.

Rückgabewert

Typ
Promise<FileSystemFileHandle | FileSystemDirectoryHandle | null>
Beschreibung
Ein Promise, das mit einem FileSystemFileHandle (bei Dateien) oder einem FileSystemDirectoryHandle (bei Ordnern) erfüllt wird. Ist der Eintrag keine Datei (kind !== "file"), wird es mit null erfüllt.

Beispiele

Dateien und Ordner per Drop unterscheiden

const dropZone = document.querySelector('#drop-zone');

// dragover abbrechen, damit ein Drop überhaupt möglich ist
dropZone.addEventListener('dragover', (e) => e.preventDefault());

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

  // WICHTIG: alle Promises synchron einsammeln, erst danach awaiten
  const promises = [...e.dataTransfer.items]
    .filter((item) => item.kind === 'file')
    .map((item) => item.getAsFileSystemHandle());

  for (const handle of await Promise.all(promises)) {
    if (!handle) continue;
    if (handle.kind === 'file') {
      console.log(`Datei: ${handle.name}`);
    } else {
      console.log(`Ordner: ${handle.name}`);
    }
  }
});
Datei: notizen.txt Ordner: projekt

Gezogene Textdatei bearbeiten und zurückspeichern

const editor = document.querySelector('#editor');
const saveBtn = document.querySelector('#save');
let fileHandle = null;

editor.addEventListener('dragover', (e) => e.preventDefault());

editor.addEventListener('drop', async (e) => {
  e.preventDefault();
  const item = e.dataTransfer.items[0];
  if (item?.kind !== 'file') return;

  const handle = await item.getAsFileSystemHandle();
  if (handle?.kind !== 'file') return;

  fileHandle = handle;
  const file = await fileHandle.getFile();
  editor.value = await file.text();
});

saveBtn.addEventListener('click', async () => {
  if (!fileHandle) return;
  // Schreibrechte anfragen (zeigt ggf. einen Browser-Dialog)
  const perm = await fileHandle.requestPermission({ mode: 'readwrite' });
  if (perm !== 'granted') return;

  const writable = await fileHandle.createWritable();
  await writable.write(editor.value);
  await writable.close();
  console.log(`${fileHandle.name} gespeichert`);
});
notizen.txt gespeichert

Gezogenen Ordner rekursiv auflisten, mit Rückfallebene

const listTree = async (dirHandle, prefix = '') => {
  for await (const [name, entry] of dirHandle.entries()) {
    console.log(`${prefix}${entry.kind === 'directory' ? '📁' : '📄'} ${name}`);
    if (entry.kind === 'directory') await listTree(entry, prefix + '  ');
  }
};

document.addEventListener('dragover', (e) => e.preventDefault());
document.addEventListener('drop', async (e) => {
  e.preventDefault();
  const items = [...e.dataTransfer.items].filter((i) => i.kind === 'file');

  // Feature-Erkennung: sonst auf getAsFile() ausweichen
  if (!('getAsFileSystemHandle' in DataTransferItem.prototype)) {
    items.map((i) => i.getAsFile()).forEach((f) => console.log(`📄 ${f.name}`));
    return;
  }

  const handles = await Promise.all(items.map((i) => i.getAsFileSystemHandle()));
  for (const h of handles) {
    if (h?.kind === 'directory') await listTree(h);
  }
});
📁 src 📄 index.js 📄 package.json

// Wichtig · Fallstricke

  • Browser-Unterstützung: Die Methode gibt es nur in Chromium-Browsern (Chrome und Edge ab 86). Firefox und Safari unterstützen sie nicht. Prüfe deshalb vorher mit 'getAsFileSystemHandle' in DataTransferItem.prototype, ob sie vorhanden ist, und weiche sonst auf getAsFile() oder webkitGetAsEntry() aus.
  • Nur in sicheren Kontexten: Die Methode funktioniert nur über HTTPS oder auf localhost.
  • Kein await vor dem Einsammeln: Nach dem ersten await ist dataTransfer.items leer bzw. ungültig. Starte deshalb alle Aufrufe synchron im Handler und warte danach gesammelt mit Promise.all().
  • Berechtigungen: Durch den Drop erhält die Seite nur Leserechte. Zum Schreiben musst du mit requestPermission({ mode: 'readwrite' }) anfragen. Dieser Aufruf braucht eine Nutzeraktion, zum Beispiel einen Klick.
  • Nicht in dragover/dragenter: Der Zugriff auf die Dateiinhalte ist nur im drop-Ereignis erlaubt.
  • Handles lassen sich in IndexedDB speichern und später wieder verwenden. Nach einem Neuladen der Seite müssen die Berechtigungen aber erneut geprüft werden.
  • Es gibt die neuere, experimentelle Variante getAsFileSystemEntry() nicht. Verwechsle die Methode nicht mit webkitGetAsEntry(), das die ältere File-and-Directory-Entries-API nutzt.