Javascript – Dateien via JSZip & FileSaver als ZIP herunterladen

Tutorial JavaScript

von Ardian Shala, 30.08.2017 - update: 31.08.2017
Mittels FileReader lassen sich Dateien aus einem Input type=file in ein ArrayBuffer konvertieren.
Ein ArrayBuffer lässt sich via JSZip als Datei einer "ZIP" Datei hinzufügen.
Anschließend lässt sich die ZIP Datei mit enthaltenen Dateien mittels FileSaver herunterladen.
Vorraussetzung:
  • FileSaver - link
  • JSZip v2.x - link
  • ES6 kompatibeler Browser / Babel / Typescript

Downloads:

JsFiddle Demo

Input vom type file definieren

Für das folgende Beispiel brauchen wir Dateien, die anschließend in eine ZIP gepackt werden sollen.
Hierfür nutzen wir ein input vom type=file womit sich clientseitig Dateien auswählen lassen.

<input type="file" multiple />

EventListener für input definieren

Sobald der „ok“ Button des FileDialog betätigt wird, sollen die Dateien eingelesen werden.
Hierfür fügen wir einen EventListener „change“ dem obigen input im „window.onload“ hinzu.

window.onload = () => {
  document.querySelector("input").addEventListener("change", (e) => {
  });
}

Hilfsklasse für Dokumente

Für die weitere Verarbeitung bietet sich das Erstellen einer Hilfsklasse an, die einen Namen und den Blob einer Datei erwartet.

class Doc {
  constructor(name, blob) {
    this.name = name;
    this.blob = blob;
  }
}

Dateien in ein Dokument Array schreiben

Das input liefert uns über das ChangeEvent „(e)“ eine „FileList“, welche nicht so schön zu iterieren ist wie ein Array.
Abhilfe liefert hier die ES6 Schreibweise, welche die Dateien in ein Array kopiert.

if (e.target.files) {
    let files = [...e.target.files];
    let documents = files.map(file => new Doc(file.name, file));
}

Zip Service Klasse erstellen

Das Einlesen der Dateien, das anschließende Hinzufügen zu einer ZIP Datei und das Herunterladen sollen in einer Hilfsklasse ausgelagert werden.
Hierfür erstellen wir eine „Service“ Klasse, mit den Methoden „saveAsZip“ und „readFile“.

class ZipService {
    saveAsZip(){
    }

    readFile(){
    }
}

Funktionen readFile ausimplementieren

Als erstes implementieren wird die Funktion „readFile“, welche den Callback eines FileReader als Promise zurückreichen soll.
Dies ermöglicht uns die Funktion mit „await“ aufzurufen und so den Code sauberer zu strukturieren.

readFile(file) {
    return new Promise((resolve, reject) => {
        let reader = new FileReader();
        reader.onload = (e) => resolve(e.target.result);
        reader.onerror = (e) => reject(e);
        reader.readAsArrayBuffer(file);
    });
}

Funktionen saveAsZip ausimplementieren

Die Funktion „saveAsZip“ erhält als Parameter die „Dokumente“, iteriert diese, liest sie über „readFile“ ein und fügt sie mittels JSZip zur zip Datei hinzu.
Das anschließende „saveAs“ von FileSaver löst den Download der zip Datei aus.

async saveAsZip(documents) {
    let zipFile = new JSZip();
    for (let document of documents) {
        try {
        let docAsArrayBuffer = await this.readFile(document.blob);          
        zipFile.file(document.name, docAsArrayBuffer);
        }
        catch(err){ console.error(err) }
    }
    let blob = zipFile.generate({ type: "blob" });
    saveAs(blob, "download.zip");
}

ZipService im EventListener erstellen und saveAs aufrufen

Nun erstellen wir eine neue Instanz von ZipService und geben die zuvor aufbereiteten Dokumente als Parameter in die Funktion.

let zipService = new ZipService();
zipService.saveAsZip(documents);

Autor: Ardian Shala

Ersteller der Webseite MuchaDev.
Frontend Entwickler mit Angular, Ionic und Backend Entwickler mit C#.