Skip to content

feat: support concurrent chunk uploads#313

Closed
TorstenDittmann wants to merge 2 commits into
mainfrom
concurrent-chunk-uploads-1-9-x
Closed

feat: support concurrent chunk uploads#313
TorstenDittmann wants to merge 2 commits into
mainfrom
concurrent-chunk-uploads-1-9-x

Conversation

@TorstenDittmann

Copy link
Copy Markdown
Contributor

This PR updates the SDK to support concurrent chunk uploads.

@TorstenDittmann

Copy link
Copy Markdown
Contributor Author

Closing in favor of focused PR #314.

@greptile-apps

greptile-apps Bot commented May 21, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds concurrent chunk uploads to the Flutter SDK, allowing up to 8 parallel HTTP requests when uploading large files, and bumps the version to 24.2.0.

  • Both client_browser.dart and client_io.dart are rewritten to use a shared nextChunk worker-pool pattern via Future.wait; params and headers are now correctly copied per chunk instead of mutating a shared map, and client_io.dart opens a fresh RandomAccessFile handle per chunk to support concurrent reads safely.
  • uploadId is extracted from the first chunk's response and forwarded via x-appwrite-id to all subsequent chunks, preserving the upload-session association that the original code maintained via a sequential loop.

Confidence Score: 4/5

Safe to merge; the concurrent worker-pool logic is correct for Dart's cooperative async model and the upload-session ID is properly forwarded to all chunks.

The core concurrency logic is sound — Dart's single-threaded event loop makes the shared nextChunk/completedChunks/uploadedBytes state safe between awaits. The main concern is that lastResponse is only updated when isUploadComplete fires; if the server ever omits chunksUploaded from intermediate responses, callers receive the first-chunk response as the upload result. There is also a minor dead parameter (index in uploadChunk) in both client files.

lib/src/client_browser.dart and lib/src/client_io.dart warrant a second look around the lastResponse update logic and the unused index parameter.

Important Files Changed

Filename Overview
lib/src/client_browser.dart Rewrites chunk upload to use a concurrent worker-pool pattern (up to 8 parallel uploads); correctly copies params/headers per chunk; lastResponse only updates when isUploadComplete fires, which could leave callers with stale first-chunk metadata if the server omits chunksUploaded; unused index param in uploadChunk.
lib/src/client_io.dart Mirrors client_browser changes; replaces single shared RandomAccessFile with per-chunk open/close, which is correct for concurrent reads; same lastResponse / unused index concerns as client_browser.
pubspec.yaml Version bump from 24.1.1 to 24.2.0.
README.md Updates dependency version reference to match the new SDK version.
docs/examples/avatars/get-screenshot.md Changes the example timezone enum value from americaNewYork to africaAbidjan; both are valid enum entries, likely an auto-generated doc update.

Reviews (1): Last reviewed commit: "Commit from GitHub Actions (Format and p..." | Re-trigger Greptile

Comment on lines +231 to +254
Future<Response> uploadChunk(
int index, int start, int end, String? id) async {
List<int> chunk = [];
final end = min(offset + chunkSize, size);
chunk = file.bytes!.getRange(offset, end).toList();
params[paramName] = http.MultipartFile.fromBytes(
chunk = file.bytes!.getRange(start, end).toList();

final chunkParams = Map<String, dynamic>.from(params);
chunkParams[paramName] = http.MultipartFile.fromBytes(
paramName,
chunk,
filename: file.filename,
);
headers['content-range'] =
'bytes $offset-${min<int>((offset + chunkSize - 1), size - 1)}/$size';
res = await call(
final chunkHeaders = Map<String, String>.from(headers);
if (id != null && id.isNotEmpty) {
chunkHeaders['x-appwrite-id'] = id;
}
chunkHeaders['content-range'] = 'bytes $start-${end - 1}/$size';

return call(
HttpMethod.post,
path: path,
headers: headers,
params: params,
headers: chunkHeaders,
params: chunkParams,
);
offset += chunkSize;
if (offset < size) {
headers['x-appwrite-id'] = res.data['\$id'];
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unused index parameter in uploadChunk

The index parameter is accepted by uploadChunk in both client_browser.dart and client_io.dart but is never referenced in either function body. All positioning is done via start/end, so the parameter appears to be dead code. Remove it or document its intended use to avoid confusion.

Comment on lines +305 to +307
if (isUploadComplete(chunkResponse)) {
lastResponse = chunkResponse;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 lastResponse falls back to the first-chunk response if the server never signals completion

lastResponse is only updated inside uploadNext when isUploadComplete returns true. If every concurrent chunk response has null or a non-num chunksUploaded field (e.g., a transient server regression), lastResponse stays as the first chunk's Response—which has stale metadata such as chunksUploaded: 1. The caller has no way to distinguish this from a genuinely incomplete upload. The old sequential loop always returned the last chunk's response regardless of field values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant