Start · Sprachen · JavaScript · Referenz · CompressionStream.writable

CompressionStream.writable

Eigenschaft

Nur-lesende Eigenschaft, die den <code>WritableStream</code> eines <code>CompressionStream</code> liefert, in den unkomprimierte Binär-Chunks zur Komprimierung geschrieben werden.

seit JavaScript Baseline 2023: Chrome 80, Firefox 113, Safari 16.4 Kategorie: webapi

Signatur

CompressionStream.writable

Beschreibung

Die Eigenschaft writable ist die Eingabeseite eines CompressionStream. Sie liefert einen WritableStream, der unkomprimierte Daten als ArrayBuffer, TypedArray (z. B. Uint8Array) oder DataView annimmt. Die komprimierten Bytes erscheinen danach auf der Ausgabeseite CompressionStream.readable.

Zusammen bilden writable und readable ein TransformStream-Paar. Deshalb lässt sich ein CompressionStream direkt an ReadableStream.prototype.pipeThrough() übergeben. Das ist der übliche Weg: Die Pipeline schreibt dann selbst in writable. Direkt über einen Writer (writable.getWriter()) arbeitet man nur, wenn man Daten von Hand und Stück für Stück einspeisen möchte, etwa aus einer Schleife oder aus Ereignissen.

Wichtig: Die komprimierten Daten auf readable sind erst vollständig, wenn die Schreibseite mit writer.close() geschlossen wurde. Erst dann schreibt der Kompressor den Abschluss des Formats (Prüfsumme und Trailer bei gzip). Außerdem sollte man readable gleichzeitig auslesen, denn der Rückstau (Backpressure) kann sonst writer.write() blockieren.

Rückgabewert

Typ
WritableStream<BufferSource>
Beschreibung
Der WritableStream, der unkomprimierte Chunks (ArrayBuffer, TypedArray, DataView) entgegennimmt. Bei jedem Zugriff kommt dieselbe Instanz zurück.

Beispiele

Text manuell über einen Writer mit gzip komprimieren

const compressText = async (text) => {
  const cs = new CompressionStream('gzip');
  const writer = cs.writable.getWriter();

  // Schreiben und Lesen parallel starten, damit der Rückstau nicht blockiert
  const resultPromise = new Response(cs.readable).arrayBuffer();

  await writer.write(new TextEncoder().encode(text));
  await writer.close(); // erst jetzt wird der gzip-Trailer geschrieben

  return new Uint8Array(await resultPromise);
};

const gz = await compressText('Hallo Welt! '.repeat(100));
console.log(gz.length, gz[0].toString(16), gz[1].toString(16));
z. B. 36 1f 8b

Mehrere Chunks nacheinander schreiben (z. B. Log-Einträge)

const cs = new CompressionStream('deflate');
const writer = cs.writable.getWriter();
const encoder = new TextEncoder();
const blobPromise = new Response(cs.readable).blob();

const logs = [
  { level: 'info', msg: 'Start' },
  { level: 'warn', msg: 'Langsame Antwort' },
  { level: 'info', msg: 'Ende' }
];

for (const entry of logs) {
  await writer.ready; // Backpressure respektieren
  await writer.write(encoder.encode(JSON.stringify(entry) + '\n'));
}
await writer.close();

const blob = await blobPromise;
console.log('Komprimiert:', blob.size, 'Bytes');
Komprimiert: 67 Bytes (Wert variiert)

Datei per pipeTo direkt in writable leiten

// <input type="file" id="datei">
const input = document.querySelector('#datei');

input.addEventListener('change', async () => {
  const file = input.files[0];
  const cs = new CompressionStream('gzip');

  const outPromise = new Response(cs.readable).blob();
  await file.stream().pipeTo(cs.writable); // schließt writable automatisch

  const gz = await outPromise;
  console.log(`${file.name}: ${file.size} → ${gz.size} Bytes`);
});
bericht.csv: 120000 → 18342 Bytes

Prüfen, ob writable gesperrt ist

const cs = new CompressionStream('gzip');
console.log(cs.writable instanceof WritableStream);
console.log(cs.writable.locked);

const writer = cs.writable.getWriter();
console.log(cs.writable.locked);
writer.releaseLock();
console.log(cs.writable.locked);
true false true false

// Wichtig · Fallstricke

  • Nur Binärdaten: Strings werden nicht angenommen. Wer einen String schreibt, bekommt einen TypeError, und der Stream geht in den Fehlerzustand. Text also vorher mit TextEncoder umwandeln oder TextEncoderStream davorschalten.
  • close() nicht vergessen: Ohne writer.close() endet readable nie, und die Ausgabe bleibt unvollständig. Bei gzip fehlen dann CRC und Trailer.
  • Deadlock-Gefahr: Wer mit await große Datenmengen schreibt, ohne readable gleichzeitig auszulesen, kann durch den Rückstau hängen bleiben. Das Lesen daher vor dem Schreiben starten.
  • Sperre: Es kann immer nur ein Writer oder eine Pipe aktiv sein. Solange writable.locked true ist, werfen getWriter() und pipeTo() einen Fehler.
  • Abbruch: writer.abort(grund) bzw. ein AbortSignal in pipeTo() versetzt auch readable in den Fehlerzustand.
  • Kompatibilität: Seit 2023 in allen großen Browsern verfügbar (Firefox ab 113, Safari ab 16.4), in Node.js global ab Version 18. Das Format 'deflate-raw' wird erst in neueren Versionen unterstützt. Für ältere Umgebungen braucht man einen Fallback wie pako oder fflate.