writeStream method

  1. @override
Future<void> writeStream(
  1. String key,
  2. Stream<List<int>> byteStream, [
  3. WriteOptions options = const WriteOptions()
])
override

Write a byte stream with optional write options.

Creates parent structure if needed. Replaces any existing object. Primary write path — write delegates to this.

options controls encryption, content type, and metadata.

Implementation

@override
Future<void> writeStream(
  String key,
  Stream<List<int>> byteStream, [
  WriteOptions options = const WriteOptions(),
]) async {
  checkNotDisposed();
  final shouldEncrypt = options.encrypt ?? encryptByDefault;

  if (!shouldEncrypt) {
    return _inner.writeStream(key, byteStream, options);
  }

  final encKey = await _resolveKey(key, options);

  // True streaming encrypt — per-chunk MACs, never buffers whole file.
  // Track original plaintext size by counting input bytes BEFORE encryption.
  // Progress is reported here, in plaintext space — the inner backend
  // only sees ciphertext counts (header + MAC overhead), which would
  // skew a progress bar sized to the source.
  var totalPlainSize = 0;
  final countingStream = byteStream.map((chunk) {
    totalPlainSize += chunk.length;
    options.onProgress?.call(totalPlainSize, null);
    return chunk;
  });

  final encryptedStream = _encryptor.encryptStream(countingStream, encKey);

  // Write encrypted stream to the inner backend.
  // Initial metadata has _metaEncrypted flag but not originalSize
  // (we don't know it yet — the stream hasn't been fully consumed).
  // onProgress is deliberately absent: the counting stream above
  // already reports it in plaintext space.
  final encOptions = WriteOptions(
    contentType: options.contentType,
    metadata: {...options.metadata, _metaEncrypted: 'true'},
  );

  await _inner.writeStream(key, encryptedStream, encOptions);

  // Stream is fully consumed now — totalPlainSize is final.
  // Patch the sidecar metadata with the correct originalSize.
  // updateMetadata only touches the .meta.json sidecar (native)
  // or the metadata fields of the IndexedDB record (web).
  // It does NOT re-read or re-write the file bytes.
  await _inner.updateMetadata(
    key,
    contentType: options.contentType,
    metadata: {
      ...options.metadata,
      _metaEncrypted: 'true',
      _metaOriginalSize: totalPlainSize.toString(),
    },
  );
}