writeStream method
Future<void>
writeStream(
- String key,
- Stream<
List< byteStream, [int> > - 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(),
},
);
}