downloadFileBytes method

Future<Uint8List> downloadFileBytes(
  1. String uuid, {
  2. int maxConcurrentChunks = kDefaultDownloadConcurrency,
  3. dynamic onProgress(
    1. int bytesDownloaded,
    2. int totalBytes
    )?,
})

Download file content as bytes (no disk I/O — needed for Web platform).

Implementation

Future<Uint8List> downloadFileBytes(
  String uuid, {
  int maxConcurrentChunks = kDefaultDownloadConcurrency,
  Function(int bytesDownloaded, int totalBytes)? onProgress,
}) async {
  api.log('Downloading file bytes: $uuid');

  final info = await api.post('/v3/file', {'uuid': uuid});
  final d = info['data'];
  final metaStr = await crypto.tryDecrypt(d['metadata'], masterKeys);
  final meta = json.decode(metaStr);
  final keyBytes = crypto.decodeUniversalKey(meta['key']);
  final chunks = int.parse(d['chunks'].toString());
  final host = 'https://egest.filen.io';
  final fileSize = meta['size'] ?? 0;

  final useConcurrency =
      maxConcurrentChunks > 1 && chunks > kSequentialDownloadChunkThreshold;

  int bytesDownloaded = 0;

  if (!useConcurrency) {
    final buffer = BytesBuilder();
    for (var i = 0; i < chunks; i++) {
      final decrypted = await _fetchChunk(host, d, uuid, i, keyBytes);
      buffer.add(decrypted);
      bytesDownloaded += decrypted.length;
      if (onProgress != null) onProgress(bytesDownloaded, fileSize);
    }
    return buffer.toBytes();
  }

  // Fetch N chunks concurrently into ordered slots, then assemble in index
  // order. The whole file is held in memory regardless (this API returns the
  // full bytes); concurrency only overlaps the network fetches.
  final slots = List<Uint8List?>.filled(chunks, null);
  final sem = ChunkSemaphore(maxConcurrentChunks);
  final inflight = <Future<void>>[];
  Object? firstError;

  for (var i = 0; i < chunks; i++) {
    if (firstError != null) break;
    await sem.acquire();
    if (firstError != null) {
      sem.release();
      break;
    }
    final idx = i;
    inflight.add(() async {
      try {
        final decrypted = await _fetchChunk(host, d, uuid, idx, keyBytes);
        slots[idx] = decrypted;
        bytesDownloaded += decrypted.length;
        if (onProgress != null) onProgress(bytesDownloaded, fileSize);
      } catch (e) {
        firstError ??= e;
      } finally {
        sem.release();
      }
    }());
  }

  await Future.wait(inflight);
  if (firstError != null) throw firstError!;

  final buffer = BytesBuilder();
  for (var i = 0; i < chunks; i++) {
    buffer.add(slots[i]!);
  }
  return buffer.toBytes();
}