Prompt and scope
Assume a 2 GB CSV, a 20 MB main-thread memory budget, and a progress update within 100 ms after each acknowledged chunk. The user can reload or go offline for 30 minutes. The client must resume without rereading the whole file, keep the interface responsive, and never claim a chunk was uploaded before the server acknowledged it. OPFS is private to the page origin; it is different from a user-visible folder handle.
What the interviewer is testing
- Choosing a storage primitive from file size, persistence, permission, and browser support.
- Moving parsing and byte I/O off the main thread without making UI state incoherent.
- Defining a resumable protocol with chunk identity, checksums, retries, and server reconciliation.
- Handling quota pressure, eviction, cancellation, privacy, and unsupported browsers.
Clarifying questions to ask
Ask whether the original file must remain available after reload, whether the server accepts out-of-order chunks, whether rows can be parsed incrementally, and which browsers are in scope. If the server can stream directly from a user-selected file, local persistence may be unnecessary; if reload recovery is mandatory, OPFS or another durable store becomes part of the design.
The 30-second answer
I would keep a small manifest in OPFS with upload_id, source fingerprint, chunk size, acknowledged ranges, and checksum state. A dedicated worker reads bounded slices, writes a durable chunk before marking it pending, and uploads with an idempotent chunk key. The server reports accepted ranges, so a retry reconciles instead of guessing. The main thread receives throttled progress messages and remains responsible for cancellation and accessible status. Feature detection, storage-quota handling, and a user-selected-file fallback protect unsupported or evicted clients.
Step-by-step deep dive
1. Choose storage and boundaries
Keep the manifest and small metadata in a durable browser store, and place large temporary chunks in OPFS. OPFS is origin-private and does not require a permission prompt for access; user-visible file handles follow a different permission model and require an explicit gesture. Use a worker for synchronous access handles or other blocking work so the main thread never waits on disk I/O.
2. Make resume a protocol, not a progress bar
Generate an uploadid and deterministic chunk numbers. Each request carries the uploadid, chunk index, byte range, length, and checksum. The server stores accepted ranges and returns them on status. A retry with the same chunk identity is idempotent; a checksum mismatch is rejected. On reload, the worker reads the manifest, asks the server for accepted ranges, and uploads only the missing set.
3. Keep UI responsive and recoverable
Read fixed-size slices, transfer only one bounded buffer at a time, and throttle progress events so rendering does not compete with parsing. Persist the manifest before starting a network attempt and after each server acknowledgement. Cancellation marks the upload locally, aborts active requests, and schedules cleanup; it must not erase the only recovery metadata before the server confirms termination.
4. Handle quota, compatibility, and security
Check available storage before copying a file and surface a recoverable “storage is full” state. If OPFS is unavailable or evicted, fall back to a user-selected file and restart from server-known ranges; never promise offline resume in that mode. Treat file names and parsed rows as untrusted, require authenticated upload authorization, and avoid exposing private local paths. Test reloads, offline periods, duplicate chunks, checksum failures, tab crashes, quota denial, and a worker restart.
A strong sample answer
I would clarify browser targets, whether rows can stream, and whether reload recovery is required. The client stores a manifest and bounded temporary chunks in OPFS, while a worker reads and uploads them using deterministic chunk identities. The server is authoritative for accepted ranges; retries reconcile against that state and verify checksums. The main thread only renders throttled progress and owns cancellation. If storage is unavailable, the UI switches to a selected-file flow with server-side resume but clearly loses offline persistence. Quota, eviction, private-origin semantics, and cleanup are observable states, not hidden exceptions.
Common mistakes
- Read the whole file into memory → a 2 GB input freezes or crashes the tab → slice bounded chunks in a worker.
- Treat a progress percentage as truth → a lost acknowledgement creates a hole or duplicate → reconcile accepted ranges with the server.
- Assume OPFS is a user-visible folder → users cannot inspect or grant the same access → explain origin-private storage and use a file handle when export is required.
- Persist only after upload success → a crash loses the next retry point → persist the manifest before attempts and after acknowledgements.
- Ignore quota and eviction → offline recovery fails without a user action → check capacity and provide an explicit fallback.
- Trust names or CSV fields → local data can inject content or leak paths → validate input and never expose local path details.
Follow-up questions and responses
The tab crashes after the server receives a chunk but before the manifest update. What now?
On restart, ask the server for accepted ranges and treat that response as authoritative. The missing local manifest entry may be uploaded again with the same chunk identity; the server must return the existing result instead of storing a duplicate.
The browser reports that OPFS storage was evicted. Do you fail the import?
Keep the upload record and explain that offline resume is no longer available. Ask the user to reselect the source file, then continue from server-accepted ranges if the source fingerprint matches; otherwise start a new upload.
Why not use a user-visible directory handle for every import?
It adds a permission and gesture flow and couples the product to a folder the user can change externally. Use it when editing or exporting the original file is required; use OPFS for private temporary chunks.
How do you avoid a worker flooding the network?
Bound in-flight chunks, pause reads when the server or browser reports backpressure, and prioritize retries over new work. Expose queue depth and retry age so the UI can explain slow progress.