ZIP-Modul (zip)¶
Das zip-Modul fasst mehrere Dokumente oder Byte-Quellen zu einem ZIP-Archiv
zusammen — optional passwortgeschützt. Damit lassen sich z.B. ein
PDF-Ausdruck und eine CSV der Formulardaten in einem Rutsch als verschlüsseltes
Archiv per E-Mail verschicken.
Immer verfügbar
zip steht — wie mail, fs, csv — in jedem Formular-Skript, Job und
REST-Service bereit. Kein Import, keine Konfiguration nötig.
Kürzer: zip.archive(…)
zip.archive(eintraege) macht dasselbe wie zip.create, liefert aber ein
Dokument:
zip.create bleibt unverändert und gibt weiterhin rohe Bytes zurück.
Schnelleinstieg¶
var meinCsvPrinter = new csv.StandardPrinter();
meinCsvPrinter.println(["Nr", "Name"]).println([1, "Müller"]);
// Einträge sammeln — jede data-Quelle darf Text, byte[], ein CSV-Printer/
// ByteSource, eine fs-Datei oder ein Zahlen-Array sein.
var bytes = zip.create([
{ name: "Ausdruck.pdf", data: pdf.render().bytes },
{ name: "daten.csv", data: meinCsvPrinter },
{ name: "hinweis.txt", data: "Bitte prüfen." }
], { password: "geheim" }); // Passwort optional
// Das Ergebnis ist ein rohes byte[] — direkt an eine Mail hängen:
var mp = new mail.MessageMultipart();
mp.addText("Im Anhang die Unterlagen als ZIP.");
mp.addBytes("dokumente.zip", bytes, "application/zip", null);
Das byte[] von zip.create(...) passt überall dort, wo Bytes erwartet werden:
mp.addBytes(...), delivery.deliver(...), fs["/ablage/x.zip"] = bytes und
ctx.attachments.add("x.zip", bytes).
Methoden¶
| Aufruf | Ergebnis |
|---|---|
zip.create(entries [, options]) |
ZIP als byte[] |
zip.deliver(entries, filename [, options]) |
ZIP ausliefern → Download-URL (String) |
zip.newArchive([options]) |
inkrementeller Builder |
zip.list(source [, options]) |
Einträge (ohne Inhalt) — Array |
zip.read(source [, options]) |
Einträge mit Inhalt — Array |
zip.extract(source, name [, options]) |
Bytes eines Eintrags (byte[] oder null) |
Der Builder aus zip.newArchive(...) hat:
| Methode | Ergebnis |
|---|---|
add(name, data) |
fügt einen Eintrag hinzu, gibt den Builder zurück (Chaining) |
bytes() / toByteArray() |
fertiges ZIP als byte[] |
deliver(filename) |
ZIP ausliefern → Download-URL |
size |
Anzahl bisher hinzugefügter Einträge |
entries¶
Ein Array von { name, data }:
name— Dateiname im Archiv. Unterordner mit/erlaubt ("belege/2026/rechnung.pdf"); führende Slashes und..-Segmente werden abgewiesen (kein „Zip-Slip").data— die Bytes. Erlaubt: Text (UTF-8),byte[](z.B.pdf.render().bytes), einByteSource/CSV-Printer (new csv.StandardPrinter()), einefs-Datei (fs["/import.csv"]) oder ein Zahlen-Array.
options¶
| Feld | Werte | Default |
|---|---|---|
password |
beliebiger Text | (keins → unverschlüsselt) |
encryption |
"zip" (ZipCrypto) · "aes" (AES) |
"zip" |
aesStrength |
128 · 192 · 256 |
256 |
Ohne password entsteht ein normales, unverschlüsseltes ZIP; die anderen
Optionen sind dann wirkungslos.
Verschlüsselung: ZipCrypto vs. AES¶
Kompromiss bewusst wählen
- ZipCrypto (
encryption:"zip", Default) — öffnet überall, auch mit den Windows-Bordmitteln (Explorer). Aber kryptografisch schwach: nicht für wirklich vertrauliche Daten geeignet. - AES-256 (
encryption:"aes") — starke Verschlüsselung. Der Empfänger braucht dafür ein Werkzeug wie 7-Zip oder WinRAR; der Windows-Explorer kann AES-ZIPs nicht öffnen.
var entries = [{ name: "hinweis.txt", data: "Bitte prüfen." }];
// Kompatibel (Default):
zip.create(entries, { password: "geheim" });
// Stark:
zip.create(entries, { password: "geheim", encryption: "aes" }); // AES-256
zip.create(entries, { password: "geheim", encryption: "aes", aesStrength: 128 });
Das Passwort selbst kommt üblicherweise aus einem Formularfeld
(ctx.form.passwort), einer Property
(ctx.params.zipPassword) o.ä. — es wird nicht protokolliert.
Builder (inkrementell)¶
Wenn die Einträge nach und nach entstehen:
var z = zip.newArchive({ password: "geheim", encryption: "aes" });
z.add("Ausdruck.pdf", pdf.render().bytes);
z.add("ordner/notiz.txt", "Text");
var bytes = z.bytes(); // oder .deliver("dokumente.zip")
add(name, data) gibt den Builder zurück (Chaining möglich), bytes() /
toByteArray() liefert das fertige byte[], deliver(filename) einen
Download-Link.
Als Download ausliefern¶
Analog zu pdf.render().deliver(...):
var entries = [{ name: "hinweis.txt", data: "Bitte prüfen." }];
var url = zip.deliver(entries, "dokumente.zip", { password: "geheim" });
view.add(new view.Link(url, "dokumente.zip herunterladen"));
Komplettes Beispiel: Ausdruck + CSV als geschütztes ZIP mailen¶
Ein vollständiger Schaltflächen-Hook — der Weg aus dem Demoformular. Er sammelt den PDF-Ausdruck plus eine CSV der Formulardaten, packt beides in ein passwortgeschütztes ZIP und verschickt es.
var to = (ctx.form.empfaenger || '').trim();
if (!to) {
view.add(new view.Message('Bitte zuerst eine Empfaenger-E-Mail eintragen.', 'ZIP-Versand'));
} else {
var pw = ctx.form.passwort || '';
var enc = ctx.form.verschluesselung || 'zip'; // 'zip' = ZipCrypto, 'aes' = AES-256
// 1) Einträge sammeln
var entries = [ { name: 'Ausdruck.pdf', data: pdf.render().bytes } ];
var p = new csv.StandardPrinter();
p.println('Feld', 'Wert');
p.println('Betreff', ctx.form.betreff || '');
entries.push({ name: 'daten.csv', data: p }); // csv.StandardPrinter ist ByteSource
// 2) ZIP bauen (mit Passwort verschlüsselt, sonst offen)
var zipBytes = zip.create(entries, pw ? { password: pw, encryption: enc } : {});
// 3) Als Anhang verschicken
var msg = new mail.Message();
if (ctx.user && ctx.user.email) { msg.from = ctx.user.email; }
msg.to = to;
msg.subject = (ctx.form.betreff || 'Unterlagen') + ' (ZIP)';
var mp = new mail.MessageMultipart();
mp.addText('Im Anhang die Unterlagen als ZIP.');
mp.addBytes('dokumente.zip', zipBytes, 'application/zip', null);
msg.content = mp;
mail.send(msg);
view.add(new view.Message('ZIP an ' + to + ' gesendet.', 'ZIP-Versand'));
}
Lesen & Entpacken¶
Ein bestehendes ZIP — z. B. aus einer Dateianlage
oder einer fs-Datei — lässt sich wieder auslesen. Die Quelle darf Bytes, ein
CSV-Printer, eine fs-Datei oder ein Zahlen-Array sein. Ein ZIP aus dem Netz
lädst du zuerst mit http.download in die Sandbox und liest es von
dort (r.bytes() beim http-Client liefert Base64-Text, keine Bytes).
var zipBytes = zip.create([{ name: "hinweis.txt", data: "Bitte prüfen." }],
{ password: "geheim" });
// im Betrieb meist: ctx.attachments.bytes(id) oder fs["/import.zip"]
// Nur die Einträge auflisten (ohne Inhalt). Bei einem verschlüsselten Archiv
// das Passwort mitgeben. zip.list/zip.read liefern eine Liste — for...of:
for (var e of zip.list(zipBytes, { password: "geheim" })) {
log.info(e.name + " — " + e.size + " Bytes" + (e.encrypted ? " (verschlüsselt)" : ""));
}
// Alles entpacken (bei verschlüsselten Archiven Passwort mitgeben):
for (var e of zip.read(zipBytes, { password: "geheim" })) {
// e = { name, size, directory, encrypted, bytes, text }
fs["/entpackt/" + e.name] = e.bytes; // z.B. in die Sandbox schreiben
}
// Eine einzelne Datei gezielt herausholen (byte[] oder null):
var pdf = zip.extract(zipBytes, "Ausdruck.pdf", { password: "geheim" });
| Feld eines gelesenen Eintrags | Bedeutung |
|---|---|
name |
Pfad im Archiv |
size |
Größe der entpackten Bytes (bei zip.list von KCForms-erzeugten Streaming-ZIPs ggf. 0 — die echte Größe liefert zip.read) |
directory |
true bei Ordner-Einträgen |
bytes |
roher Inhalt (byte[]) |
text |
Inhalt als UTF-8-Text (bequem für Text-Dateien) |
Zip-Bomb-Schutz
zip.read/zip.extract deckeln die entpackte Gesamtgröße über dasselbe
maxTotalBytes-Limit — ein Archiv, das sich beim Entpacken „aufbläht",
bricht mit einer Fehlermeldung ab, statt den Speicher zu erschöpfen.
Verschlüsselte Archive brauchen das Passwort — auch beim Auflisten
KCForms schreibt Streaming-ZIPs (mit Data-Descriptor); der Reader muss auch
zum bloßen Weiterspringen die Einträge entschlüsseln. Deshalb braucht auch
zip.list bei verschlüsselten Archiven options.password — nicht nur
zip.read/zip.extract.
Grenzen¶
Zum Schutz vor Speicher-Erschöpfung gelten zwei Obergrenzen (konfigurierbar unter
kcforms.zip): Höchstzahl Einträge (maxEntries, Default 1000) und
Gesamtgröße der unkomprimierten Eingabebytes (maxTotalBytes, Default
200 MB). Wird eine Grenze überschritten, bricht der Aufruf mit einer sprechenden
Fehlermeldung ab.
Beispielformular¶
Das Beispiel „ZIP-Versand (Demo)" (zip-demo; installieren unter
System-Administration → Formular-Bausteine → Beispiele, danach im Katalog
Demo → Integration) zeigt den kompletten Weg: PDF-Ausdruck (+ optional CSV) zu einem
— auf Wunsch passwortgeschützten — ZIP zusammenfassen und per E-Mail versenden
(zipMail), als Download bereitstellen (zipDownload) oder gleich wieder
auslesen (zipRoundtrip — demonstriert zip.list/zip.read/zip.extract).
Fehlermeldungen¶
| Meldung / Verhalten | Ursache |
|---|---|
zip: keine Einträge — das Archiv wäre leer. |
entries/Builder ohne einen einzigen Eintrag |
zip: Eintrag #N braucht die Felder { name, data }. |
Ein Array-Element hat kein name oder data |
zip: data muss Text, byte[], … sein. |
data ist ein nicht unterstützter Typ |
zip: Eintragsname darf kein '..'-Segment enthalten |
„Zip-Slip"-Schutz — kein Ausbrechen aus dem Zielordner |
zip: Höchstzahl Einträge (…) überschritten. |
mehr als maxEntries Einträge |
zip: Gesamtgröße überschreitet das Limit … |
Eingabebytes > maxTotalBytes (beim Packen) |
zip.read: entpackte Gesamtgröße überschreitet das Limit … |
Zip-Bomb-Schutz beim Entpacken |
zip.read/…: Entpacken fehlgeschlagen — Wrong Password … |
falsches/fehlendes Passwort für ein verschlüsseltes Archiv |
zip.extract(…) liefert null |
Der gesuchte Eintragsname ist nicht im Archiv |
zip.deliver: DeliveryService nicht verfügbar … |
Auslieferung in diesem Kontext nicht möglich — stattdessen zip.create(...) nutzen |
Alle Fehler kommen als sprechende Ausnahme zurück; ein Schaltflächen-Hook zeigt sie als Fehlermeldung an, statt „erledigt" zu melden.
Siehe auch¶
- Mail — Anhänge,
mp.addBytes(...) - CSV —
csv.StandardPrinterals Byte-Quelle - PDF & Auslieferung —
pdf.render().bytes - Dateianlagen —
ctx.attachments