Skip to content

Real per-chunk upload progress (web + native, 0.6.11)#43

Merged
ehsan6sha merged 2 commits into
mainfrom
feat/upload-progress-stream
Jun 17, 2026
Merged

Real per-chunk upload progress (web + native, 0.6.11)#43
ehsan6sha merged 2 commits into
mainfrom
feat/upload-progress-stream

Conversation

@ehsan6sha

Copy link
Copy Markdown
Member

Real per-chunk upload progress so apps can show a true percentage instead of a time-based estimate — on web + native (iOS/Android/Windows). Workspace 0.6.10 → 0.6.11.

SDK (fula-client)

The chunked upload paths now emit cumulative bytes the moment each content chunk's PUT lands (count-proportional: completed_chunks · total / N):

  • Web/chunked: put_object_flat_with_progress.
  • Native fresh: put_object_encrypted_resumable_with_cancel_and_progress.
  • Native resume: resume_upload_with_cancel_and_progressseeds the counter with the already-uploaded chunk count, so the bar continues mid-way and still reaches 100% even though only the remaining chunks are re-PUT.

The existing public put_object_encrypted_resumable_with_cancel / resume_upload_with_cancel keep their signatures (external test callers depend on them) — they're now thin wrappers delegating to the *_and_progress bodies with progress = None. Progress is an Option<Arc<dyn Fn(u64,u64)+Send+Sync>>; emitted from inside the spawned chunk tasks via a shared AtomicU64 so it reflects real concurrent completion order. No change to the upload/crypto data path — only added emit calls + new wrapper methods.

Flutter bridge (fula-flutter)

Progress is surfaced by polling an opaque handle, mirroring the proven CancelHandle / MasterHealthEvent pattern — not StreamSink (stream-handler codegen has never been exercised through the external publish pipeline; can't verify locally):

  • ProgressHandle + create_progress_handle() + poll_progress() -> UploadProgress.
  • put_flat_with_progress (web + native), put_flat_resumable_from_path_with_progress + resume_flat_upload_from_path_with_progress (native, take &CancelHandle + &ProgressHandle).

Apps poll poll_progress on a timer while the upload future runs. The handle's Arc lifecycle is the cleanup (no global registry, no leak on early-return/panic).

Tests

  • chunk_put_progress.rs — web path reaches 100% (max == total).
  • resumable_put_progress.rs — native fresh (max == total) + resume seed-correctness (max == total is reachable only when the counter is seeded; the partial manifest is produced by a real failed attempt, not hand-built — F1 BAO rejects synthetic manifests).
  • upload_progress_real_server_e2e.rs — live-master E2E for both paths (3 MiB multi-chunk, asserts progress reaches 100% + byte-identical round-trip).
  • Full fula-client --features test-fault-injection suite green (F1 nonce-reuse ordering, resumable_forest_persistence, v2 bridge tests unperturbed by the refactor).

Compile gates

cargo check green: fula-flutter native (the Send+Sync gate for the callback across the 16 spawned chunk tasks), fula-client wasm32, fula-flutter wasm32.

Consumer note (FxFiles)

SDK reports content 0 → total; cumulative hits total when the last chunk's PUT returns, before the index PUT + forest-flush tail — UIs should cap at <100% until their own completion signal (reuse the mobile clamp).

🤖 Generated with Claude Code

ehsan6sha and others added 2 commits June 16, 2026 22:41
Per-chunk cumulative upload progress for the put_flat (web) path via put_object_chunked_internal. Resumable (native) path + FRB bindings to follow.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… 0.6.11

Apps can now show a true upload percentage instead of a time-based estimate,
on web + native (iOS/Android/Windows). The SDK emits cumulative bytes as each
content chunk's PUT lands; Flutter surfaces it via a pollable handle.

SDK (fula-client, encryption.rs):
- put_object_flat_with_progress — web/chunked path.
- put_object_encrypted_resumable_with_cancel_and_progress — native fresh.
- resume_upload_with_cancel_and_progress — native resume; SEEDS the counter
  with already-uploaded chunks so the bar continues mid-way and still reaches
  100% though only the remaining chunks are re-PUT.
  The existing public _with_cancel / resume_upload_with_cancel keep their
  signatures (external test callers depend on them) and are now thin wrappers
  delegating with progress=None. Emit is count-proportional, fired from inside
  each spawned chunk task via a shared AtomicU64. No change to the upload or
  crypto data path — only added emit calls + new wrapper methods.

FRB (fula-flutter, forest.rs):
- Polling transport, NOT StreamSink: ProgressHandle (opaque, mirrors
  CancelHandle) + create_progress_handle + poll_progress(-> UploadProgress).
  StreamSink stream-handler codegen has never been exercised through the
  external publish pipeline and can't be verified locally; polling is the
  proven pattern (MasterHealthEvent, CancelHandle).
- put_flat_with_progress (web+native); put_flat_resumable_from_path_with_progress
  + resume_flat_upload_from_path_with_progress (native; take &CancelHandle +
  &ProgressHandle). Apps poll poll_progress on a timer during the upload.

Tests:
- resumable_put_progress.rs — native fresh (max==total) + resume
  seed-correctness (max==total reachable only when seeded; partial manifest
  produced by a real failed attempt, not hand-built).
- upload_progress_real_server_e2e.rs — live-master E2E, both paths, 3 MiB
  multi-chunk, progress reaches 100% + byte-identical round-trip (verified:
  12 events, max == file size on both paths).
- chunk_put_progress.rs (web) from the prior wip commit.
- Full fula-client --features test-fault-injection suite green (208 lib +
  all integration; F1 nonce-reuse ordering + resumable_forest_persistence +
  v2 bridge tests unperturbed by the wrapper refactor).

cargo check green: fula-flutter native (Send+Sync gate), fula-client wasm32,
fula-flutter wasm32. Workspace version 0.6.10 -> 0.6.11.

Consumer note (FxFiles): SDK reports content 0 -> total; cumulative hits total
when the last chunk's PUT returns, before the index PUT + forest-flush tail —
UIs should cap at <100% until their own completion signal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ehsan6sha ehsan6sha merged commit cd35d31 into main Jun 17, 2026
8 checks passed
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