ZIP-Archive im Browser mit JSZip erstellen
Übergeben Sie die Objekte vom Typ File aus dem Browser an JSZip,
erzeugen Sie ein ZIP als Blob und übergeben Sie diesen Blob an einen
Download-Link. Mit der vollständigen Seite unten können Nutzer mehrere lokale Dateien auswählen
oder hineinziehen und als archive.zip herunterladen. Dabei wird der Fortschritt
angezeigt und die Gesamtgröße der Eingabedateien begrenzt.
Dieses Beispiel lädt keine ausgewählten Dateien hoch und benötigt keinen Komprimierungsserver. Es lädt JSZip von einem CDN. Zum Laden der Bibliothek ist daher eine Internetverbindung erforderlich. Die Komprimierung und ihr Speicherbedarf bleiben im Browser; dieser Ansatz eignet sich für kleinere Dateisammlungen, die bereits auf dem Gerät des Nutzers liegen.
Das vollständige Browserbeispiel ausführen
Speichern Sie dies als neue Datei index.html in einem leeren Ordner und
öffnen Sie sie anschließend in der Desktopversion von Chrome. Weder ein Build-Schritt noch ein
lokaler Server ist nötig. Das Skript legt JSZip auf Version 3.10.2 fest; dieses Beispiel wurde in
Chrome 153 unter macOS getestet.
Wählen Sie einzelne Dateien über die Dateiauswahl aus oder ziehen Sie sie in die Feldgruppe. Jede Auswahl ersetzt die vorherige Dateisammlung, deren Namen unter der Dateiauswahl erscheinen. Ordner werden nicht durchsucht. Klicken Sie auf Create ZIP, warten Sie auf die Download-Anfrage und öffnen Sie dann die ZIP-Datei über die Downloads Ihres Browsers.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Create a ZIP with JSZip</title>
</head>
<body>
<h1>Create a ZIP</h1>
<fieldset id="dropZone">
<legend>Choose files or drop them here</legend>
<label for="fileInput">Files to archive</label>
<input type="file" id="fileInput" multiple />
<p id="selection">No files selected.</p>
<button type="button" id="zipButton">Create ZIP</button>
</fieldset>
<p id="progress" role="status">Select files totaling at most 100 MiB.</p>
<script src="https://cdn.jsdelivr.net/npm/jszip@3.10.2/dist/jszip.min.js"></script>
<script>
const fileInput = document.getElementById('fileInput')
const dropZone = document.getElementById('dropZone')
const selection = document.getElementById('selection')
const progress = document.getElementById('progress')
let selectedFiles = []
let busy = false
function selectFiles(files) {
if (busy) return
selectedFiles = Array.from(files)
// The names below remain visible; clearing the picker allows reselection.
fileInput.value = ''
selection.textContent = selectedFiles.length
? `Selected files: ${selectedFiles.map((file) => file.name).join(', ')}`
: 'No files selected.'
progress.textContent = 'Ready to create ZIP.'
}
fileInput.addEventListener('change', () => selectFiles(fileInput.files))
dropZone.addEventListener('dragover', (event) => {
event.preventDefault()
event.dataTransfer.dropEffect = busy ? 'none' : 'copy'
})
dropZone.addEventListener('drop', (event) => {
event.preventDefault()
selectFiles(event.dataTransfer.files)
})
async function createZip() {
if (busy) return
const files = selectedFiles.slice()
if (files.length === 0) {
progress.textContent = 'Select or drop files first.'
return
}
if (typeof JSZip === 'undefined') {
progress.textContent = 'JSZip could not load. Check your connection and reload.'
return
}
const totalSize = files.reduce((sum, file) => sum + file.size, 0)
if (totalSize > 100 * 1024 * 1024) {
progress.textContent = 'Select at most 100 MiB of files.'
return
}
const names = new Set()
for (const file of files) {
if (names.has(file.name)) {
progress.textContent = `Duplicate filename: ${file.name}. Rename it first.`
return
}
names.add(file.name)
}
busy = true
dropZone.disabled = true
progress.textContent = 'Generating ZIP…'
try {
const zip = new JSZip()
for (const file of files) zip.file(file.name, file)
const blob = await zip.generateAsync(
{
type: 'blob',
compression: 'DEFLATE',
compressionOptions: { level: 6 },
},
({ percent }) => {
progress.textContent = `Generating ZIP: ${Math.round(percent)}%`
},
)
const url = URL.createObjectURL(blob)
// Leave time for the browser to consume the URL before releasing it.
setTimeout(() => URL.revokeObjectURL(url), 60_000)
const link = document.createElement('a')
link.href = url
link.download = 'archive.zip'
document.body.appendChild(link)
link.click()
link.remove()
progress.textContent = 'ZIP download requested. Check your browser’s downloads.'
} catch {
progress.textContent = 'Could not create ZIP. Reselect the files and try again.'
} finally {
busy = false
dropZone.disabled = false
}
}
document.getElementById('zipButton').addEventListener('click', createZip)
</script>
</body>
</html>
Wählen Sie für einen schnellen Test eine Textdatei und ein Bild mit unterschiedlichen Namen aus.
Die heruntergeladene ZIP-Datei sollte beide mit unverändertem Inhalt im Stammverzeichnis enthalten.
Erzeugen Sie sie erneut, um einen weiteren Download anzufordern.
archive.zip ist ein vorgeschlagener Name: Der Browser bestimmt den Speicherort
und mögliche Rückfragen zum Umbenennen oder Ersetzen, wenn dieser Name bereits existiert.
Die Seite erzwingt kein Überschreiben.
Dateien direkt hinzufügen und den Erstellungsfortschritt anzeigen
Sowohl ein Dateiauswahlfeld als auch das Ablegen von Dateien liefern über die File API Objekte vom
Typ File. Die
Methode file(name, data) von JSZip
akzeptiert diese direkt, da File eine Art von
Blob ist. Sie benötigen weder einen separaten Wrapper für
FileReader noch eine base64-Konvertierung, um lokale Dateien zum Archiv
hinzuzufügen.
generateAsync()
gibt ein Promise für das fertige Archiv zurück. Hier erzeugt die Methode einen Blob und verwendet
DEFLATE auf Stufe sechs. Der Callback-Wert percent beschreibt die
Archiverstellung, nicht den Fortschritt eines Downloads. Sobald der Blob bereit ist, macht eine
temporäre URL ihn für den Link verfügbar; der Timer gibt diese URL nach der Download-Anfrage frei.
„ZIP download requested“ unterscheidet sich bewusst von „gespeichert“. Die
Eigenschaft download des Links
bestätigt nicht, dass ein Download stattgefunden hat. Browsereinstellungen können ihn blockieren,
oder der Nutzer kann den Speicherdialog abbrechen. Prüfen Sie die Download-Übersicht, wenn keine
Datei erscheint.
Nur einen ZIP-Vorgang gleichzeitig zulassen
Die Sperre busy wird vor Beginn der Komprimierung gesetzt. Solange das
Promise aussteht, deaktiviert die Feldgruppe die Dateiauswahl und die Schaltfläche, und
selectFiles() ignoriert neu abgelegte Dateien oder Änderungsereignisse. Der
Vorgang behält seine ursprüngliche Auswahl bei. So kann ein zweiter Klick weder ein weiteres Archiv
starten noch die Fortschrittsmeldung ersetzen. Nach Erfolg oder Fehler aktiviert
finally die Bedienelemente wieder. Wenn Sie eine andere Auswahl verwenden
möchten, warten Sie, bis der aktuelle Vorgang beendet ist, und wählen Sie dann die neuen Dateien
aus oder ziehen Sie sie hinein.
JSZip aktualisiert einen bestehenden Eintrag, wenn derselbe Name erneut hinzugefügt wird. Die Duplikatprüfung verhindert, dass eine ausgewählte Datei unbemerkt eine andere ersetzt. Das ist relevant, wenn Dateien aus verschiedenen Ordnern denselben Basisnamen haben. Benennen Sie eine davon um, bevor Sie es erneut versuchen. Namen, die sich nur in der Groß- und Kleinschreibung unterscheiden, können beim Entpacken auf einem Dateisystem, das diese Unterscheidung ignoriert, weiterhin kollidieren.
Wenn das Laden vom CDN fehlschlägt, fordert die Seite Sie auf, die Verbindung zu prüfen. Wenn das Lesen oder Komprimieren fehlschlägt, fordert sie Sie auf, die Dateien erneut auszuwählen. Ein fehlgeschlagener Vorgang fordert keinen Download an. Für eine Seite, die offline funktionieren muss, liefern Sie das JSZip-Bundle in der festgelegten Version zusammen mit Ihrer eigenen Seite aus, statt sich auf das CDN zu verlassen.
Umgang mit großen Dateien und Leistungsaspekte
Die Prüfung auf 100 MiB ist eine beispielhafte Eingabegrenze, keine Garantie für die Kapazität des
Browsers. Bei JSZip
hält generateAsync() das vollständige Ergebnis im Arbeitsspeicher,
und auch das Lesen und Komprimieren der Eingabedateien benötigt Arbeitsspeicher. Viele kleine Dateien
verursachen ebenfalls zusätzlichen Aufwand. Wählen Sie eine niedrigere Grenze, wenn Ihre Zielgeräte
diese Last nicht problemlos bewältigen können.
DEFLATE kann Text mit vielen Wiederholungen verkleinern, bringt bei bereits komprimierten Bildern
und Videos aber möglicherweise wenig. Wenn Sie diese Dateien nur bündeln müssen, unterstützt JSZip
auch compression: 'STORE'. Dabei wird die Komprimierung übersprungen. Die Komprimierung
in einen Web Worker auszulagern, kann den Hauptthread der Seite entlasten. Dadurch schreibt
generateAsync({ type: 'blob' }) sein Ergebnis jedoch nicht als Stream auf die Festplatte. Große
Exporte benötigen eine andere Strategie für Arbeitsspeicher und Ausgabe.
Browserkompatibilität
Die hier geprüfte Zielumgebung ist die Desktopversion von Chrome unter macOS. Andere Browser
benötigen die File API, Promises, async/await,
Blob-URLs sowie Unterstützung für Downloads von diesen URLs. Behalten Sie die Dateiauswahl für
Geräte bei, auf denen sich Dateien nicht hineinziehen lassen, und testen Sie Auswahl,
Fehlerbehebung und tatsächliche Downloads in jedem Browser, den Sie unterstützen möchten. Die
Browserunterstützung der JSZip-Bibliothek allein belegt nicht, dass dort der gesamte Download-Ablauf
der Seite funktioniert.
