multipartFileUpload method

Future<PixelBinImage?> multipartFileUpload(
  1. File file,
  2. SignedDetails signedDetails,
  3. int chunkSize,
  4. int concurrency,
)

Implementation

Future<PixelBinImage?> multipartFileUpload(File file,
    SignedDetails signedDetails, int chunkSize, int concurrency) async {
  final fileSize = await file.length();
  final chunkSizeInBytes = 1024 * chunkSize;
  final semaphore =
      Semaphore(concurrency, onEachCompleted: (int milliseconds) {
    print('Chunk uploaded in $milliseconds ms');
  });

  var partNumber = 0;
  var offset = 0;
  var errorOccurred = false;
  var parts = <int>[];

  List<Future<void>> uploadFutures = [];

  while (offset < fileSize && !errorOccurred) {
    final end = (offset + chunkSizeInBytes).clamp(0, fileSize);
    final chunk = await readChunk(file, offset, end - offset);

    partNumber++;
    final url = '${signedDetails.url}&partNumber=$partNumber';
    final request = http.MultipartRequest('PUT', Uri.parse(url));

    signedDetails.fields.forEach((key, value) {
      request.fields[key] = value;
    });

    request.files
        .add(http.MultipartFile.fromBytes('file', chunk, filename: 'chunk'));

    final client = http.Client();

    uploadFutures.add(semaphore.withPermit(() async {
      final response = await client.send(request);
      final responseBody = await response.stream.bytesToString();
      print("Response: ${response.statusCode} $responseBody");
      if (response.statusCode >= 200 && response.statusCode < 300) {
        parts.add(partNumber);
      } else {
        errorOccurred = true;
        throw Exception(
            "Request Error: ${response.statusCode} $responseBody");
      }
    }));

    offset = end;
  }

  await Future.wait(uploadFutures);

  if (!errorOccurred) {
    return completeMultipartUpload(
        signedDetails.url, signedDetails.fields, partNumber);
  }
  throw Exception("Unknown Error occurred");
}