Dateien in JavaScript lesen

Die Auswahl und Interaktion mit Dateien auf dem lokalen Gerät des Nutzers ist eine der am häufigsten verwendeten Funktionen im Web. Nutzer können Dateien auswählen und auf einen Server hochladen, z. B. wenn sie Fotos teilen oder Steuerdokumente einreichen. Außerdem können Websites sie lesen und bearbeiten, ohne die Daten über das Netzwerk übertragen zu müssen. Auf dieser Seite wird beschrieben, wie Sie mit JavaScript mit Dateien interagieren.

Die moderne File System Access API

Die File System Access API bietet eine Möglichkeit, Dateien und Verzeichnisse auf dem lokalen System des Nutzers zu lesen und zu schreiben. Sie ist in den meisten Chromium-basierten Browsern wie Chrome und Edge verfügbar. Weitere Informationen finden Sie unter File System Access API.

Da die File System Access API nicht mit allen Browsern kompatibel ist, empfehlen wir die Verwendung von browser-fs-access, einer Hilfsbibliothek, die die neue API überall dort verwendet, wo sie verfügbar ist, und bei Nichtverfügbarkeit auf ältere Ansätze zurückgreift.

Auf klassische Weise mit Dateien arbeiten

In diesem Leitfaden erfahren Sie, wie Sie mithilfe alter JavaScript-Methoden mit Dateien interagieren.

Dateien auswählen

Es gibt zwei Hauptmethoden zum Auswählen von Dateien: über das HTML-Eingabeelement und über eine Drag-and-drop-Zone.

HTML-Eingabeelement

Am einfachsten können Nutzer Dateien über das Element <input type="file"> auswählen, das in allen gängigen Browsern unterstützt wird. Wenn ein Nutzer darauf klickt, kann er über die integrierte Benutzeroberfläche zur Dateiauswahl seines Betriebssystems eine Datei oder mehrere Dateien auswählen, sofern das Attribut multiple enthalten ist. Wenn der Nutzer die Auswahl einer oder mehrerer Dateien abgeschlossen hat, wird das change-Ereignis des Elements ausgelöst. Sie können über event.target.files, ein FileList-Objekt, auf die Liste der Dateien zugreifen. Jeder Eintrag in FileList ist ein File-Objekt.

<!-- The `multiple` attribute lets users select multiple files. -->
<input type="file" id="file-selector" multiple>
<script>
  const fileSelector = document.getElementById('file-selector');
  fileSelector.addEventListener('change', (event) => {
    const fileList = event.target.files;
    console.log(fileList);
  });
</script>

Im folgenden Beispiel kann ein Nutzer über die integrierte Benutzeroberfläche zur Dateiauswahl seines Betriebssystems mehrere Dateien auswählen und dann jede ausgewählte Datei in der Konsole protokollieren.

Dateitypen einschränken, die Nutzer auswählen können

In einigen Fällen möchten Sie die Dateitypen einschränken, die Nutzer auswählen können. Eine Bildbearbeitungs-App sollte beispielsweise nur Bilder und keine Textdateien akzeptieren. Wenn Sie Einschränkungen für Dateitypen festlegen möchten, fügen Sie dem Eingabeelement das Attribut accept hinzu, um anzugeben, welche Dateitypen zulässig sind:

<input type="file" id="file-selector" accept=".jpg, .jpeg, .png">

Benutzerdefiniertes Drag-and-drop

In einigen Browsern ist das Element <input type="file"> auch ein Drop-Ziel, über das Nutzer Dateien per Drag-and-drop in Ihre App ziehen können. Dieses Drop-Ziel ist jedoch klein und kann schwer zu verwenden sein. Nachdem Sie die Hauptfunktionen mit einem <input type="file">-Element bereitgestellt haben, können Sie stattdessen eine große, benutzerdefinierte Drag-and-drop-Oberfläche bereitstellen.

Dropzone auswählen

Die Drop-Oberfläche hängt vom Design Ihrer Anwendung ab. Sie können auch nur einen Teil des Fensters als Drop-Zone verwenden, aber auch das gesamte Fenster.

Screenshot von Squoosh, einer Web-App zur Bildkomprimierung
Mit Squish wird das gesamte Fenster zu einer Drop-Zone.

Mit der Bildkomprimierungs-App Squoosh können Nutzer ein Bild an eine beliebige Stelle im Fenster ziehen und auf Bild auswählen klicken, um das <input type="file">-Element aufzurufen. Unabhängig davon, was Sie als Drop-Zone auswählen, muss für den Nutzer klar erkennbar sein, dass er Dateien auf diese Oberfläche ziehen kann.

Dropzone definieren

Wenn Sie ein Element als Drag-and-drop-Zone aktivieren möchten, erstellen Sie Listener für zwei Ereignisse: dragover und drop. Das Ereignis dragover aktualisiert die Browser-Benutzeroberfläche, um visuell anzuzeigen, dass durch die Drag-and-drop-Aktion eine Kopie der Datei erstellt wird. Das Ereignis drop wird ausgelöst, nachdem der Nutzer die Dateien auf die Oberfläche abgelegt hat. Wie beim Eingabeelement können Sie über event.dataTransfer.files, ein FileList-Objekt, auf die Liste der Dateien zugreifen. Jeder Eintrag in FileList ist ein File-Objekt.

const dropArea = document.getElementById('drop-area');

dropArea.addEventListener('dragover', (event) => {
  event.stopPropagation();
  event.preventDefault();
  // Style the drag-and-drop as a "copy file" operation.
  event.dataTransfer.dropEffect = 'copy';
});

dropArea.addEventListener('drop', (event) => {
  event.stopPropagation();
  event.preventDefault();
  const fileList = event.dataTransfer.files;
  console.log(fileList);
});

Mit event.stopPropagation() und event.preventDefault() wird das Standardverhalten des Browsers beendet und stattdessen Ihr Code ausgeführt. Ohne sie würde der Browser von Ihrer Seite wegnavigieren und die Dateien öffnen, die der Nutzer in das Browserfenster abgelegt hat.

Eine Live-Demo finden Sie unter Benutzerdefiniertes Drag-and-drop.

Was ist mit Verzeichnissen?

Leider gibt es keine gute Möglichkeit, mit JavaScript auf ein Verzeichnis zuzugreifen.

Mit dem Attribut webkitdirectory des Elements <input type="file"> kann der Nutzer ein oder mehrere Verzeichnisse auswählen. Sie wird von den meisten gängigen Browsern unterstützt, mit Ausnahme von Firefox für Android und Safari für iOS.

Wenn Drag-and-drop aktiviert ist, kann ein Nutzer versuchen, ein Verzeichnis in die Drop-Zone zu ziehen. Wenn das Drop-Ereignis ausgelöst wird, enthält es ein File-Objekt für das Verzeichnis, bietet aber keinen Zugriff auf die Dateien im Verzeichnis.

Dateimetadaten lesen

Das File-Objekt enthält Metadaten zur Datei. Die meisten Browser geben den Dateinamen, die Dateigröße und den MIME-Typ an. Je nach Plattform können verschiedene Browser jedoch unterschiedliche oder zusätzliche Informationen bereitstellen.

function getMetadataForFileList(fileList) {
  for (const file of fileList) {
    // Not supported in Safari for iOS.
    const name = file.name ? file.name : 'NOT SUPPORTED';
    // Not supported in Firefox for Android or Opera for Android.
    const type = file.type ? file.type : 'NOT SUPPORTED';
    // Unknown cross-browser support.
    const size = file.size ? file.size : 'NOT SUPPORTED';
    console.log({file, name, type, size});
  }
}

In der input-type-file-Demo können Sie sich das ansehen.

Inhalt einer Datei lesen

Mit FileReader kannst du den Inhalt eines File-Objekts in den Arbeitsspeicher lesen. Du kannst FileReader anweisen, eine Datei als Array-Buffer, Daten-URL oder Text zu lesen:

function readImage(file) {
  // Check if the file is an image.
  if (file.type && !file.type.startsWith('image/')) {
    console.log('File is not an image.', file.type, file);
    return;
  }

  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    img.src = event.target.result;
  });
  reader.readAsDataURL(file);
}

In diesem Beispiel wird eine vom Nutzer bereitgestellte File gelesen, in eine Daten-URL konvertiert und dann mithilfe dieser Daten-URL in einem img-Element angezeigt. Wie Sie prüfen, ob der Nutzer eine Bilddatei ausgewählt hat, erfahren Sie in der Demo read-image-file.

Fortschritt des Dateilesevorgangs beobachten

Beim Lesen großer Dateien kann es hilfreich sein, dem Nutzer zu zeigen, wie weit der Lesevorgang fortgeschritten ist. Verwenden Sie dazu das Ereignis progress von FileReader. Das Ereignis progress hat zwei Properties: loaded (die gelesene Menge) und total (die zu lesende Menge).

function readFile(file) {
  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    const result = event.target.result;
    // Do something with result
  });

  reader.addEventListener('progress', (event) => {
    if (event.loaded && event.total) {
      const percent = (event.loaded / event.total) * 100;
      console.log(`Progress: ${Math.round(percent)}`);
    }
  });
  reader.readAsDataURL(file);
}

Hero-Image von Vincent Botta von Unsplash