Signatur
Beschreibung
CompressionStream gehört zur Compression Streams API. Damit kannst du Daten komprimieren, ohne eine externe Bibliothek wie pako oder fflate einzubinden. Das Objekt funktioniert wie ein TransformStream: Auf der Seite writable schreibst du unkomprimierte Bytes hinein, auf der Seite readable liest du die komprimierten Bytes aus. Meistens hängst du es mit ReadableStream.prototype.pipeThrough() in eine Stream-Kette ein.
Typische Anwendungsfälle:
- große JSON-Daten oder Logs vor dem Hochladen komprimieren,
- Daten platzsparend in IndexedDB oder im Cache ablegen,
.gz-Dateien direkt im Client erzeugen, z. B. für einen Export.
Der Konstruktor new CompressionStream(format) erwartet das Format als Zeichenkette. Die Instanz hat nur zwei schreibgeschützte Eigenschaften: readable (ein ReadableStream mit komprimierten Uint8Array-Chunks) und writable (ein WritableStream, der BufferSource-Chunks annimmt, also ArrayBuffer, TypedArray oder DataView). Zeichenketten musst du vorher kodieren, z. B. mit TextEncoderStream oder mit Blob.prototype.stream().
Das Gegenstück zum Entpacken ist DecompressionStream. Die API steht in Fenstern und in Web Workern zur Verfügung. Weil sie auf Streams aufbaut, kann sie auch sehr große Datenmengen verarbeiten, ohne alles auf einmal in den Speicher zu laden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $format Pflicht | "gzip" | "deflate" | "deflate-raw" | Das Kompressionsformat: "gzip" ist GZIP (RFC 1952), "deflate" ist DEFLATE mit ZLIB-Hülle (RFC 1950), "deflate-raw" ist reines DEFLATE ohne Header und Prüfsumme (RFC 1951). Bei einem anderen Wert wird ein TypeError geworfen. |
Rückgabewert
readable (ReadableStream<Uint8Array>) und writable (WritableStream<BufferSource>).Beispiele
Zeichenkette mit gzip komprimieren und Größe vergleichen
const compressText = async (text, format = 'gzip') => {
// Blob liefert einen Bytestrom, pipeThrough hängt die Kompression an
const stream = new Blob([text]).stream()
.pipeThrough(new CompressionStream(format));
// Response sammelt den Stream bequem in einem ArrayBuffer
const buffer = await new Response(stream).arrayBuffer();
return new Uint8Array(buffer);
};
const json = JSON.stringify(Array.from({ length: 1000 }, (_, i) => ({ id: i, status: 'aktiv' })));
const gz = await compressText(json);
console.log(`Original: ${json.length} Bytes, gzip: ${gz.byteLength} Bytes`);
console.log(gz[0].toString(16), gz[1].toString(16)); // gzip-Magic-Bytes
Datei im Browser als .gz herunterladen
// <input type="file" id="datei">
const input = document.querySelector('#datei');
input.addEventListener('change', async () => {
const file = input.files[0];
if (!file) return;
const compressed = file.stream().pipeThrough(new CompressionStream('gzip'));
const blob = await new Response(compressed).blob();
const url = URL.createObjectURL(blob);
const a = Object.assign(document.createElement('a'), {
href: url,
download: `${file.name}.gz`
});
a.click();
URL.revokeObjectURL(url);
console.log(`${file.size} → ${blob.size} Bytes`);
});
Hin und zurück: komprimieren und mit DecompressionStream wieder entpacken
const text = 'Hallo Welt! '.repeat(500);
// Komprimieren
const packed = await new Response(
new Blob([text]).stream().pipeThrough(new CompressionStream('deflate-raw'))
).arrayBuffer();
// Entpacken und direkt wieder als Text dekodieren
const unpacked = await new Response(
new Blob([packed]).stream()
.pipeThrough(new DecompressionStream('deflate-raw'))
.pipeThrough(new TextDecoderStream())
).text();
console.log(packed.byteLength, unpacked === text);
Direkt über writable/readable schreiben (ohne pipeThrough)
const cs = new CompressionStream('gzip');
const writer = cs.writable.getWriter();
// Schreiben und Lesen laufen parallel, damit der Rückstau keinen Deadlock auslöst
const resultPromise = new Response(cs.readable).arrayBuffer();
const encoder = new TextEncoder();
for (const zeile of ['Zeile 1\n', 'Zeile 2\n', 'Zeile 3\n']) {
await writer.write(encoder.encode(zeile));
}
await writer.close();
const result = await resultPromise;
console.log('Komprimiert:', result.byteLength, 'Bytes');
// Wichtig · Fallstricke
- Nur Bytes erlaubt: Wenn du eine Zeichenkette direkt mit
writer.write('text')schreibst, führt das zu einemTypeError, und der Stream geht in den Fehlerzustand. Kodiere Text vorher mitTextEncoderoderTextEncoderStream. - Rückstau (Backpressure): Wenn du über
writableschreibst, musst dureadablegleichzeitig auslesen. Wartest du vor dem Lesen auf allewrite()-Aufrufe, kann der Code hängen bleiben, sobald die interne Warteschlange voll ist. - Formate: Nur
gzip,deflateunddeflate-rawsind verlässlich verfügbar. Brotli oder Zstandard werden nicht breit unterstützt.deflate-rawkam später dazu (Chrome 103, Firefox 113, Safari 16.4). Eine Kompressionsstufe kannst du nicht einstellen. - Kein ZIP-Archiv:
gzipkomprimiert genau einen Datenstrom. Für.zip-Dateien mit mehreren Einträgen brauchst du zusätzlich eine Bibliothek oder eine eigene Container-Logik, die intern z. B.deflate-rawnutzen kann. - Upload: Wenn du den komprimierten Stream mit
fetch()alsbodysendest, brauchst duduplex: 'half'. Das unterstützen nur Chromium-Browser. Für breite Kompatibilität sammelst du den Stream besser erst in einemBlobund sendest ihn mit dem HeaderContent-Encoding: gzip. Der Server muss diesen Header auch verarbeiten können. - Kompatibilität: Seit Mai 2023 in allen großen Browsern verfügbar, in Node.js ab Version 18 global (vorher über
stream/web), außerdem in Deno und Bun. Vorab prüfen lässt sich die Unterstützung mit'CompressionStream' in globalThis. - Sicherheit: Wenn du geheime Daten zusammen mit Eingaben eines Angreifers komprimierst, kann die Größe des Ergebnisses Rückschlüsse auf die geheimen Daten zulassen (CRIME/BREACH-ähnliche Angriffe).