downloadFileRange method

Future<Uint8List> downloadFileRange(
  1. String uuid, {
  2. int? rangeStart,
  3. int? rangeEnd,
})

Download file with range support.

Implementation

Future<Uint8List> downloadFileRange(
  String uuid, {
  int? rangeStart,
  int? rangeEnd,
}) async {
  api.log('Downloading file range: $uuid ($rangeStart-$rangeEnd)');

  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';

  const chunkSize = 1048576;
  final startChunk = rangeStart != null ? rangeStart ~/ chunkSize : 0;
  final endChunk = rangeEnd != null ? rangeEnd ~/ chunkSize : chunks - 1;

  final buffer = BytesBuilder();

  for (var i = startChunk; i <= endChunk && i < chunks; i++) {
    final r = await api.client
        .get(Uri.parse('$host/${d['region']}/${d['bucket']}/$uuid/$i'));
    if (r.statusCode != 200) {
      throw Exception('Chunk download failed: ${r.statusCode}');
    }

    var chunkBytes = await crypto.decryptData(r.bodyBytes, keyBytes);

    // Trim the tail of the last chunk, then the head of the first chunk.
    // Trimming end-before-start keeps both offsets relative to the chunk
    // start — required when the whole range lies within a single chunk
    // (start == end), where the previous if/else only trimmed the head.
    if (i == endChunk && rangeEnd != null) {
      final endOffset = rangeEnd % chunkSize + 1;
      if (endOffset < chunkBytes.length) {
        chunkBytes = chunkBytes.sublist(0, endOffset);
      }
    }
    if (i == startChunk && rangeStart != null) {
      chunkBytes = chunkBytes.sublist(rangeStart % chunkSize);
    }
    buffer.add(chunkBytes);
  }

  return buffer.toBytes();
}