Compression Streams API: Gzip im Browser ohne Bibliothek
AI generated
JS
() =>
JavaScript · Browser-APIs · Performance
Compression Streams API
Gzip im Browser ohne Bibliothek

Fuer Gzip-Kompression im Browser wurde jahrelang pako oder eine WASM-Portierung von zlib nachgeladen, oft mehrere hundert Kilobyte allein fuer die Bibliothek. Die Compression Streams API macht das ueberfluessig: CompressionStream und DecompressionStream stehen nativ im Browser bereit.

15 Min. Lesezeit Compression Streams API Gzip Streams API Performance

1. Warum native Kompression im Browser lange fehlte

Server komprimieren HTTP-Antworten seit Jahrzehnten selbstverstaendlich per Gzip oder Brotli, doch der Browser selbst konnte lange Zeit keine beliebigen Daten komprimieren oder dekomprimieren. Wer im Client Gzip-Daten erzeugen oder lesen wollte, etwa um ein grosses JSON-Objekt vor dem Upload zu verkleinern, musste zwangslaeufig eine JavaScript- oder WebAssembly-Bibliothek wie pako nachladen, die zlib im Browser nachbildet.

Die Compression Streams API schliesst diese Luecke direkt auf Plattform-Ebene. Sie stellt zwei Klassen bereit, CompressionStream und DecompressionStream, die als TransformStream im Sinne der Streams API funktionieren und Gzip- sowie Deflate-Kompression nativ, also mit der hochoptimierten C++-Implementierung des Browsers, durchfuehren, ganz ohne zusaetzliches JavaScript-Bundle.

2. Grundlagen: CompressionStream als TransformStream

CompressionStream wird mit einem Format-String instanziiert, aktuell 'gzip', 'deflate' oder 'deflate-raw', und verhaelt sich danach wie jeder andere TransformStream: Er hat eine writable-Seite, in die unkomprimierte Bytes geschrieben werden, und eine readable-Seite, aus der komprimierte Bytes gelesen werden koennen. Diese Streaming-Natur ist der entscheidende Unterschied zu klassischen Bibliotheken, die meist den kompletten Input im Speicher halten muessen.

Der ueblichste Weg, einen Blob oder ein ReadableStream zu komprimieren, ist pipeThrough(), das die Quelle direkt durch den CompressionStream leitet. Das Ergebnis ist wieder ein ReadableStream, der beispielsweise direkt in einen neuen Blob ueberfuehrt oder an fetch() als Request-Body uebergeben werden kann, ohne dass die kompletten Daten je vollstaendig im Arbeitsspeicher liegen muessen.


// Text komprimieren und als Blob erhalten
async function compressText(text) {
  const stream = new Blob([text]).stream();
  const compressedStream = stream.pipeThrough(new CompressionStream('gzip'));
  return new Response(compressedStream).blob();
}

const compressed = await compressText('Ein sehr langer JSON-String...');
console.log('Komprimierte Groesse:', compressed.size, 'Bytes');

3. DecompressionStream: Daten wieder lesbar machen

DecompressionStream funktioniert spiegelbildlich: Er nimmt Gzip- oder Deflate-komprimierte Bytes auf der writable-Seite entgegen und liefert die urspruenglichen, unkomprimierten Bytes auf der readable-Seite. Das Format muss beim Instanziieren exakt zum Format passen, mit dem urspruenglich komprimiert wurde, ansonsten wirft der Stream einen Fehler beim Lesen.

Ein haeufiger Anwendungsfall ist das direkte Dekomprimieren einer Server-Antwort, die bewusst bereits komprimiert ausgeliefert wird, etwa weil der Server ein eigenes Kompressionsformat oberhalb der normalen HTTP-Content-Encoding-Verhandlung nutzt, oder das Auslesen von zuvor selbst komprimierten und in IndexedDB abgelegten Daten.


// Komprimierten Blob wieder in Text zurueckverwandeln
async function decompressToText(compressedBlob) {
  const stream = compressedBlob.stream()
    .pipeThrough(new DecompressionStream('gzip'));
  return new Response(stream).text();
}

const original = await decompressToText(compressed);
console.log(original);

4. Praxisfall: grosse Uploads komprimieren

Ein konkreter Einsatzfall ist das Komprimieren grosser Textdaten, etwa CSV-Exporte oder umfangreiche JSON-Payloads, vor dem Upload an einen Server, der Gzip-komprimierte Request-Bodies akzeptiert. Statt Megabytes an unkomprimierten Rohdaten zu uebertragen, reduziert Gzip typischen Text-Content haeufig auf ein Zehntel bis ein Fuenftel der urspruenglichen Groesse, was besonders bei mobilen Verbindungen spuerbar Zeit spart.

Wichtig ist dabei, den passenden Content-Encoding-Header zu setzen, damit der Server weiss, dass er den Body vor der Verarbeitung dekomprimieren muss. Die meisten modernen Backend-Frameworks unterstuetzen eingehende Gzip-komprimierte Bodies bereits nativ, sodass auf Serverseite meist keine zusaetzliche Logik noetig ist.


// Grossen JSON-Export komprimiert hochladen
async function uploadCompressed(data) {
  const json = JSON.stringify(data);
  const compressedStream = new Blob([json]).stream()
    .pipeThrough(new CompressionStream('gzip'));

  const compressedBlob = await new Response(compressedStream).blob();

  return fetch('/api/export', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Content-Encoding': 'gzip',
    },
    body: compressedBlob,
  });
}

5. Praxisfall: IndexedDB-Speicherplatz sparen

IndexedDB unterliegt browser-spezifischen Speicherquoten, die je nach verfuegbarem Festplattenplatz und Nutzungsverhalten variieren. Bei Offline-First-Anwendungen, die grosse Mengen an Text- oder JSON-Daten lokal vorhalten, etwa fuer eine vollstaendige Offline-Kopie eines Produktkatalogs, lohnt es sich, Eintraege vor dem Speichern zu komprimieren, um die effektiv nutzbare Kapazitaet deutlich zu erhoehen.

Da CompressionStream Bytes statt Strings verarbeitet, muessen Textdaten zunaechst ueber TextEncoder in ein Uint8Array umgewandelt werden. Beim Auslesen aus IndexedDB kehrt sich der Prozess um: DecompressionStream liefert wieder Bytes, die per TextDecoder zurueck in einen String verwandelt werden, bevor sie als JSON geparst werden.


// Komprimiert in IndexedDB ablegen und wieder auslesen
async function compressForStorage(obj) {
  const bytes = new TextEncoder().encode(JSON.stringify(obj));
  const stream = new Blob([bytes]).stream()
    .pipeThrough(new CompressionStream('gzip'));
  return new Uint8Array(await new Response(stream).arrayBuffer());
}

async function decompressFromStorage(compressedBytes) {
  const stream = new Blob([compressedBytes]).stream()
    .pipeThrough(new DecompressionStream('gzip'));
  const text = await new Response(stream).text();
  return JSON.parse(text);
}

// Verwendung mit einem IndexedDB-Object-Store:
const compressed = await compressForStorage(largeProductCatalog);
await productStore.put({ id: 'catalog', data: compressed });

6. Vergleich zu pako und JSZip

pako ist eine reine JavaScript-Portierung von zlib und laeuft komplett im Hauptthread oder in einem selbst eingerichteten Worker, waehrend die native Compression Streams API auf der browserinternen C++-Implementierung basiert und dadurch in der Regel spuerbar schneller ist, insbesondere bei groesseren Datenmengen, und keinerlei zusaetzliches Bundle-Gewicht mitbringt.

JSZip loest ein anderes Problem, naemlich das Erzeugen und Lesen von ZIP-Archiven mit mehreren Dateien und einer Verzeichnisstruktur, und bleibt dafuer weiterhin die richtige Wahl, da die Compression Streams API nur rohe Gzip/Deflate-Kompression bietet, keine Archivformat-Logik. Fuer den einfachen Fall, einzelne Datenstroeme zu komprimieren, ist die native API jedoch klar die schlankere und schnellere Loesung.

Ein weiterer praktischer Unterschied zeigt sich beim Testen und Debuggen: Da pako reines JavaScript ist, lassen sich Zwischenzustaende bei Bedarf mit dem Debugger inspizieren, waehrend die native Implementierung als Black Box arbeitet und nur Ein- und Ausgabe der Streams beobachtbar sind. Fuer die allermeisten Anwendungsfaelle ist das kein Nachteil, da die Kompressions-Logik selbst kaum jemals angepasst werden muss, kann aber bei sehr spezifischen Debugging-Anforderungen den Ausschlag fuer pako geben.

Auch bei der Wartungslast unterscheiden sich beide Ansaetze deutlich: pako als externe Abhaengigkeit muss regelmaessig auf Sicherheitsluecken geprueft und aktualisiert werden, waehrend die native API automatisch mit jedem Browser-Update auf dem aktuellen, vom Hersteller gepflegten Stand bleibt, ganz ohne eigenes Zutun im Projekt.

7. Browser-Unterstuetzung und Feature Detection

Die Compression Streams API wird von allen aktuellen Versionen von Chrome, Edge, Firefox und Safari unterstuetzt, die Unterstuetzung ist mittlerweile deutlich breiter als etwa bei der Navigation API oder Background Sync. Fuer aeltere Browser-Versionen empfiehlt sich dennoch eine einfache Feature-Detection, bevor die API verwendet wird.

Ist CompressionStream nicht verfuegbar, kann als Fallback weiterhin pako geladen werden, allerdings nur dynamisch per import(), sodass Nutzer moderner Browser nicht das zusaetzliche Bundle-Gewicht tragen muessen. Dieses Muster, moderne API zuerst, Bibliothek als Lazy-Fallback, reduziert die durchschnittliche Bundle-Groesse fuer die Mehrheit der Nutzer erheblich.


// Feature Detection mit Lazy-Fallback
async function getCompressor() {
  if (typeof CompressionStream !== 'undefined') {
    return { native: true };
  }
  const pako = await import('pako');
  return { native: false, pako };
}

8. Performance-Hinweise fuer den produktiven Einsatz

Da CompressionStream und DecompressionStream vollstaendig auf Streams basieren, lohnt es sich, sie bei sehr grossen Datenmengen tatsaechlich als Stream zu verarbeiten statt, wie in den einfachen Beispielen oben, ueber new Response(stream).blob() den kompletten Inhalt zunaechst wieder im Speicher zu sammeln. Ein direktes pipeTo() in einen Ziel-Stream, etwa den Body eines fetch()-Requests, vermeidet unnoetige Zwischenspeicherung.

Fuer sehr kleine Datenmengen, etwa kurze Strings unter einigen hundert Bytes, lohnt sich Kompression oft nicht, da der Gzip-Header-Overhead die eingesparten Bytes wieder auffrisst oder sogar uebersteigt. Eine einfache Faustregel ist, Kompression erst ab etwa 1 bis 2 KB Rohdatenmenge einzusetzen, darunter bringt sie in der Praxis meist keinen Vorteil.

Bei wiederholter Kompression aehnlicher Datenstrukturen, etwa vieler kleiner JSON-Objekte mit gleichem Schema, lohnt es sich zudem, mehrere Objekte vor der Kompression zu einem einzigen Batch zusammenzufassen, statt jedes einzeln zu komprimieren. Gzip profitiert von wiederkehrenden Mustern innerhalb eines Datenstroms, sodass ein gebuendelter Batch in Summe deutlich besser komprimiert als dieselbe Datenmenge in vielen kleinen Einzelaufrufen mit jeweils eigenem Kompressions-Header.

9. Fazit: native Kompression als Standardwerkzeug

Die Compression Streams API macht externe Kompressions-Bibliotheken fuer den Standardfall, Gzip- oder Deflate-Kompression einzelner Datenstroeme, weitgehend ueberfluessig. Sie ist schneller, verursacht kein zusaetzliches Bundle-Gewicht und fuegt sich nahtlos in die bestehende Streams-API-Landschaft des Browsers ein.

Fuer komplexere Anwendungsfaelle wie ZIP-Archive mit mehreren Dateien bleiben spezialisierte Bibliotheken wie JSZip weiterhin notwendig, doch fuer Upload-Kompression und platzsparende IndexedDB-Speicherung ist die native API heute die richtige erste Wahl. Die folgende Tabelle vergleicht die wichtigsten Eigenschaften.

Merkmal Compression Streams API pako JSZip
Bundle-Groesse 0 KB, nativ im Browser ~45 KB minifiziert ~95 KB minifiziert
Format Gzip, Deflate, Deflate-Raw Gzip, Deflate, Zlib ZIP-Archive
Streaming-faehig Ja, echtes TransformStream Teilweise, mit Zusatzcode Nein, meist komplett im Speicher
Einsatzfall Einzelne Datenstroeme komprimieren Fallback fuer aeltere Browser Mehrere Dateien in einem Archiv

Mironsoft

Moderne Browser-APIs, Performance und wartbares JavaScript

JavaScript, das im echten Browser robust bleibt, nicht nur im Tutorial?

Wir prüfen bestehenden Frontend-Code auf veraltete Patterns, unnötige Bibliotheken und Performance-Fallen und ersetzen sie durch moderne, native Browser-APIs, die weniger Bundle-Gewicht und weniger Wartungslast bedeuten.

Code-Review

Veraltete Patterns, unnötige Dependencies und Memory Leaks systematisch aufspüren.

Performance-Optimierung

Bundle-Größe, Ladezeit und Runtime-Performance mit modernen APIs verbessern.

Modernisierung

Native Browser-APIs statt schwerer Bibliotheken gezielt einführen.

10. Zusammenfassung

Compression Streams API: Das Wichtigste auf einen Blick

CompressionStream

TransformStream, komprimiert Bytes nativ als Gzip oder Deflate

DecompressionStream

Gegenstueck, dekomprimiert Bytes zurueck in den Originalzustand

Einsatzfaelle

Upload-Kompression und platzsparende IndexedDB-Speicherung

Gegenueber pako

Kein zusaetzliches Bundle-Gewicht, native C++-Geschwindigkeit

11. FAQ: Compression Streams API: Das Wichtigste auf einen Blick

1Was macht die Compression Streams API konkret?
Sie stellt CompressionStream und DecompressionStream bereit, zwei TransformStream-Klassen, die Gzip- oder Deflate-Kompression nativ im Browser durchfuehren, ohne externe Bibliothek.
2Muss ich Text vor der Kompression umwandeln?
Ja, CompressionStream verarbeitet Bytes, kein Text direkt. Strings muessen zuerst per TextEncoder oder ueber einen Blob in Byte-Form gebracht werden.
3Wie komprimiere ich einen Blob am einfachsten?
Mit blob.stream().pipeThrough(new CompressionStream('gzip')) und anschliessend new Response(stream).blob(), um wieder einen fertigen Blob zu erhalten.
4Kann ich damit ZIP-Dateien erzeugen?
Nein, die API bietet nur rohe Gzip/Deflate-Kompression ohne Archivformat-Logik. Fuer ZIP-Archive mit mehreren Dateien bleibt eine Bibliothek wie JSZip noetig.
5Ist die native API schneller als pako?
In der Regel ja, da sie auf der hochoptimierten C++-Implementierung des Browsers basiert statt auf reinem JavaScript, besonders bei groesseren Datenmengen.
6Welche Formate unterstuetzt CompressionStream?
Aktuell gzip, deflate und deflate-raw. Weitere Formate wie Brotli sind zum Zeitpunkt der Erstellung dieses Artikels nicht Teil der Spezifikation.
7Lohnt sich Kompression fuer sehr kleine Datenmengen?
Meist nicht. Unter etwa 1 bis 2 KB Rohdatenmenge frisst der Gzip-Header-Overhead die eingesparten Bytes haeufig wieder auf.
8Wie setze ich den richtigen Header beim Upload?
Der Content-Encoding-Header muss auf gzip gesetzt werden, damit der Server weiss, dass er den Body vor der Verarbeitung dekomprimieren muss.
9Welche Browser unterstuetzen die API?
Alle aktuellen Versionen von Chrome, Edge, Firefox und Safari. Fuer aeltere Versionen empfiehlt sich eine Feature-Detection mit Lazy-Fallback auf pako.
10Warum lohnt sich Kompression fuer IndexedDB-Daten?
IndexedDB unterliegt browser-spezifischen Speicherquoten. Komprimierte Eintraege erhoehen die effektiv nutzbare Kapazitaet deutlich, besonders bei grossen Offline-Datensaetzen.