FrontendPro

109Implement a streaming upload manager supporting chunking, bounded concurrency, progress, retry, and cancellation

hardAbout 60 minutesCodinguploadsblobsconcurrencyabortsignal

Problem

Uploading a 2 GB video in one request fails on any network hiccup and shows no progress. Implement uploadFile(file, options), which uploads a Blob or File in chunks:

await uploadFile(file, {
  chunkSize: 5 * 1024 * 1024,
  concurrency: 3,
  retries: 2,
  uploadChunk: ({ index, start, end, blob, signal }) =>
    fetch(`/upload/${uploadId}/${index}`, { method: "PUT", body: blob, signal }).then((r) => {
      if (!r.ok) throw new Error(`HTTP ${r.status}`);
    }),
  onProgress: ({ loaded, total, percent }) => (bar.value = percent),
  signal: controller.signal,
});

Options: chunkSize, concurrency = 3, retries = 2, uploadChunk, onProgress = () => {}, signal, retryDelay = (attempt) => 100 * 2 ** attempt and sleep = (ms) => new Promise(…) (injectable for tests).

Requirements

  • Chunk i covers bytes [i * chunkSize, min(size, (i + 1) * chunkSize)); blob is that slice.
  • At most concurrency chunks upload at once.
  • Retry a failed chunk up to retries times, waiting retryDelay(attempt) between tries (attempt 0 for the first retry). Never retry after cancellation.
  • onProgress({ loaded, total, percent }) after each chunk finishes; loaded only counts finished chunks, so it never goes backwards; percent is rounded and reaches 100.
  • If a chunk still fails after its retries, abort the other in-flight chunks, start no new ones, and reject with an Error whose message is Chunk {i} failed after {n} attempts and whose cause is the last error.
  • If signal aborts, abort in-flight chunks, start no new ones and reject with signal.reason.
  • Resolve with { chunks, size }. An empty file resolves immediately with { chunks: 0, size: 0 }.

Try it yourself

Write your solution, run it, then submit to check it against the tests.

solution.jsJavaScript

Press Run or ⌘ Enter to execute your code.

Step-by-step

count = Math.ceil(size / chunkSize). Chunk boundaries are pure arithmetic, and file.slice(start, end) creates a lightweight view without reading the bytes into memory.

Unlock the full solution

What's inside

  • Step-by-step
  • Approach
  • Solution
  • Complexity
  • Edge cases
  • Follow-up questions
  • Tests

Includes 2 code listings, 6 edge cases, 4 follow-up questions.

One-time payment by UPI, card or net banking. Launch price.

All 150 answersOne payment, yours forever