Start · Sprachen · JavaScript · Referenz · CompressionStream

CompressionStream

Schnittstelle

Ein <code>CompressionStream</code> ist ein Transform-Stream, der einen Bytestrom direkt im Browser nach <code>gzip</code>, <code>deflate</code> oder <code>deflate-raw</code> komprimiert.

seit JavaScript Baseline 2023: Chrome 80, Edge 80, Firefox 113, Sa Kategorie: webapi

Signatur

interface CompressionStream (implements GenericTransformStream)

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

Typ
CompressionStream
Beschreibung
Eine neue Instanz mit den Eigenschaften 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
Original: 27781 Bytes, gzip: 2846 Bytes (Werte ungefähr) 1f 8b

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`);
});
z. B. 1048576 → 183422 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);
z. B. 28 true

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');
Komprimiert: 43 Bytes (ungefähr)

// Wichtig · Fallstricke

  • Nur Bytes erlaubt: Wenn du eine Zeichenkette direkt mit writer.write('text') schreibst, führt das zu einem TypeError, und der Stream geht in den Fehlerzustand. Kodiere Text vorher mit TextEncoder oder TextEncoderStream.
  • Rückstau (Backpressure): Wenn du über writable schreibst, musst du readable gleichzeitig auslesen. Wartest du vor dem Lesen auf alle write()-Aufrufe, kann der Code hängen bleiben, sobald die interne Warteschlange voll ist.
  • Formate: Nur gzip, deflate und deflate-raw sind verlässlich verfügbar. Brotli oder Zstandard werden nicht breit unterstützt. deflate-raw kam später dazu (Chrome 103, Firefox 113, Safari 16.4). Eine Kompressionsstufe kannst du nicht einstellen.
  • Kein ZIP-Archiv: gzip komprimiert 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-raw nutzen kann.
  • Upload: Wenn du den komprimierten Stream mit fetch() als body sendest, brauchst du duplex: 'half'. Das unterstützen nur Chromium-Browser. Für breite Kompatibilität sammelst du den Stream besser erst in einem Blob und sendest ihn mit dem Header Content-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).