Signatur
Beschreibung
Ein CompressionStream ist ein Transform-Stream und besteht aus zwei Seiten: Über writable schreibt man unkomprimierte Bytes hinein, und über readable kommen die komprimierten Bytes im gewählten Format ("gzip", "deflate" oder "deflate-raw") heraus. Die Eigenschaft readable gibt dabei immer dieselbe ReadableStream-Instanz zurück.
Meist greift man nicht direkt auf readable zu, sondern übergibt das ganze CompressionStream-Objekt an ReadableStream.prototype.pipeThrough(). Diese Methode verbindet die Quelle mit writable und liefert readable als Ergebnis zurück. Direkt mit readable arbeitet man, wenn man Daten manuell über einen Writer einspeist oder den Ausgabestrom selbst mit getReader(), for await…of oder pipeTo() weiterverarbeiten will.
Die Chunks, die aus readable kommen, sind immer Uint8Array-Objekte. Ihre Größe hängt von der Implementierung ab und entspricht nicht unbedingt der Größe der Eingabe-Chunks. Die letzten komprimierten Bytes, etwa die gzip-Prüfsumme und die Längenangabe, erscheinen erst, wenn die writable-Seite geschlossen wurde.
Rückgabewert
Uint8Array-Chunks.Beispiele
Text mit pipeThrough() gzip-komprimieren
const compressText = async (text) => {
const stream = new Blob([text]).stream()
.pipeThrough(new CompressionStream('gzip')); // liefert cs.readable
const buffer = await new Response(stream).arrayBuffer();
return new Uint8Array(buffer);
};
const input = 'Hallo Welt! '.repeat(1000);
const gz = await compressText(input);
console.log(`Original: ${input.length} Bytes, komprimiert: ${gz.byteLength} Bytes`);
console.log(gz[0].toString(16), gz[1].toString(16)); // gzip-Magic-Bytes
Manuell schreiben und readable per Reader auslesen
const cs = new CompressionStream('deflate');
const writer = cs.writable.getWriter();
const encoder = new TextEncoder();
// Schreiben läuft parallel zum Lesen, damit kein Rückstau blockiert
const writing = (async () => {
await writer.write(encoder.encode('{"user":"anna",'));
await writer.write(encoder.encode('"role":"admin"}'));
await writer.close(); // erst jetzt werden die letzten Bytes ausgegeben
})();
const reader = cs.readable.getReader();
const chunks = [];
for (;;) {
const { done, value } = await reader.read();
if (done) break;
console.log('Chunk:', value.constructor.name, value.byteLength);
chunks.push(value);
}
await writing;
console.log('Gesamt:', chunks.reduce((n, c) => n + c.byteLength, 0), 'Bytes');
Komprimierten Upload per fetch senden
const uploadCompressed = async (file) => {
const cs = new CompressionStream('gzip');
file.stream().pipeTo(cs.writable); // Eingabeseite füllen
const response = await fetch('/api/upload', {
method: 'POST',
headers: { 'Content-Encoding': 'gzip' },
body: cs.readable, // Ausgabeseite als Request-Body streamen
duplex: 'half' // für Streaming-Bodies erforderlich
});
return response.ok;
};
// const ok = await uploadCompressed(document.querySelector('input[type=file]').files[0]);
Node.js: readable mit for await…of lesen
// Node.js 18+ – CompressionStream ist global verfügbar
const source = new Blob(['Protokollzeile\n'.repeat(500)]).stream();
const compressed = source.pipeThrough(new CompressionStream('deflate-raw'));
let total = 0;
for await (const chunk of compressed) {
total += chunk.byteLength;
}
console.log('Komprimierte Größe:', total);
// Wichtig · Fallstricke
- Schreibgeschützt:
readablelässt sich nicht neu zuweisen. ProCompressionStreamgibt es genau einen Ausgabestrom, und der ist nach dem Lesen verbraucht. Für neue Daten legt man eine neue Instanz an. - Sperre: Nach
getReader(),pipeTo()oder der Übergabe alsfetch-Body ist der Strom gesperrt (locked === true). Ein zweiter Zugriff wirft einenTypeError. Mittee()kann man den Strom verzweigen. - Rückstau (Backpressure): Wer mit
writer.write()schreibt und jedesawaitabwartet, ohne gleichzeitig ausreadablezu lesen, kann hängen bleiben. Lesen und Schreiben müssen deshalb parallel laufen. - Unvollständige Ausgabe: Solange die
writable-Seite nicht geschlossen ist, wirdreadablenicht beendet, und die Prüfsumme bzw. der Abschlussblock fehlt. Das Ergebnis wäre dann kein gültiges gzip/deflate. - Fehler: Wird die Eingabe abgebrochen oder schreibt man etwas anderes als
BufferSource-Chunks (z. B. Strings), gerätreadablein den Fehlerzustand. Strings vorher mitTextEncoderumwandeln. - Kompatibilität:
"deflate-raw"wird erst ab Chrome 103, Firefox 113 und Safari 16.4 unterstützt. Streaming-Request-Bodies beifetch(duplex: 'half') funktionieren derzeit nur in Chromium-Browsern und brauchen HTTP/2.