upload<T> method

  1. @override
Future<Result<T>> upload<T>(
  1. String path, {
  2. required List<UploadFile> files,
  3. Map<String, String>? fields,
  4. JsonParser<T>? parser,
  5. ProgressCallback? onProgress,
  6. RequestOptions? options,
})
override

Handles large multipart file uploads with progress tracking.

Implementation

@override
Future<Result<T>> upload<T>(
  String path, {
  required List<UploadFile> files,
  Map<String, String>? fields,
  JsonParser<T>? parser,
  ProgressCallback? onProgress,
  RequestOptions? options,
}) async {
  final builder = MultipartBodyBuilder();
  fields?.forEach(builder.addField);

  for (final f in files) {
    builder.addFile(f, await f.readBytes());
  }

  final (body, totalBytes) = builder.build();

  final effectiveBase = options?.baseUrl ?? _config.baseUrl;
  final uri = _buildUri(effectiveBase, path, null);
  final headers = _mergeHeaders(_config.defaultHeaders, options?.headers);
  headers[HttpHeaders.contentTypeHeader] = builder.contentType;
  headers[HttpHeaders.contentLengthHeader] = totalBytes.toString();

  HttpClientRequest req;
  try {
    req = await _http.openUrl('POST', uri);
  } on SocketException catch (e) {
    return Failure(ConnectionException('Cannot reach ${uri.host}', cause: e));
  }

  headers.forEach(req.headers.set);

  // Stream with progress.
  const chunkSize = 65536; // 64 KiB
  var sent = 0;
  for (var offset = 0; offset < body.length; offset += chunkSize) {
    final end = (offset + chunkSize).clamp(0, body.length);
    req.add(body.sublist(offset, end));
    sent += end - offset;
    onProgress?.call(sent, totalBytes);
  }

  late HttpClientResponse response;
  try {
    response = await req.close();
  } on SocketException catch (e) {
    return Failure(
      ConnectionException('Upload failed: ${e.message}', cause: e),
    );
  }

  final rawBody = await _readBody(response);
  final responseHeaders = <String, String>{};
  response.headers.forEach((n, v) => responseHeaders[n] = v.join(', '));

  final responseCtx = ResponseContext(
    statusCode: response.statusCode,
    headers: responseHeaders,
    body: rawBody,
    request: RequestContext(uri: uri, method: 'POST', headers: headers),
  );

  return _parseResponse<T>(responseCtx, parser);
}