diff --git a/README.md b/README.md index 0a95a05..51b7404 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Chunked File Upload Example -This is a reference implementation for chunked, parallel, and resumable file uploads using a minimal API and vanilla JavaScript. The implementation was developed within the constraints of using on-premise local disk storage hosted behind a reverse proxy with payload size limits that would otherwise prevent handling large file uploads. +This is a reference implementation for chunked, concurrent, and resumable file uploads using a minimal API and vanilla JavaScript. The implementation was developed within the constraints of using on-premise local disk storage hosted behind a reverse proxy with payload size limits that would otherwise prevent handling large file uploads. ## Architecture @@ -8,7 +8,7 @@ The storage is designed around direct random-access offset writing. As data chun This approach makes file assembly basically instant. Finalizing a file takes less than 1 ms and operates with O(1) time complexity because the server only needs to atomically rename `upload.part` to its destination name using `File.Move`. This eliminates the need for background worker services, disk-copying loops, or complex progress-tracking channels between the server and client. -The program uses a two-tiered SHA-256 verification model alongside deterministic session hashing to provide end-to-end data integrity across parallel chunk transmissions: +The program uses a two-tiered SHA-256 verification model alongside deterministic session hashing to provide end-to-end data integrity across concurrent chunk transmissions: - **Per-Chunk Verification**: On the client, SHA-256 hashes are calculated lazily for each chunk right before transmission using `crypto.subtle.digest`. Upon receipt, the server streams the chunk payload, independently computes its SHA-256 digest, and validates it against the client's hash using constant-time byte comparison (`CryptographicOperations.FixedTimeEquals`). Once validated, the chunk is written to disk alongside a `{index}-{hash}.done` marker file. - **Composite End-to-End File Verification**: When all chunks finish uploading, the client orders the individual chunk hashes, joins them into a comma-separated string (`hash_0,hash_1,..., hash_n-1`), and computes a final composite SHA-256 hash. When `/Files/{fileId}/complete` is invoked, the server independently recalculates the composite hash from its stored `.done` marker files. The atomic `File.Move` rename executes only if the composite hashes match, guaranteeing that no chunks were corrupted, omitted, or reassembled out of sequence. @@ -25,7 +25,7 @@ Once files are successfully uploaded, the program supports immediate media range - **Session Initialization (`POST /Files/{fileId}`)**: Client computes a GUID from the file's metadata and requests a session. Returns `200 OK` for new uploads or `409 Conflict` if a session already exists. - **Resumability Check (`GET /Files/{fileId}/status`)**: Server scans for existing `{index}-{hash}.done` marker files and returns an array of completed chunk indices. -- **Parallel Chunk Uploads (`POST /Files/{fileId}/upload`)**: Worker pool sends chunks concurrently. Chunks are verified against their SHA-256 hash and written asynchronously into `upload.part` at `index * chunkSize`. +- **Concurrent Chunk Uploads (`POST /Files/{fileId}/upload`)**: Worker pool sends chunks concurrently. Chunks are verified against their SHA-256 hash and written asynchronously into `upload.part` at `index * chunkSize`. - **Completion (`POST /Files/{fileId}/complete`)**: Server validates the composite hash of all completed chunks and renames `upload.part` to the final filename. - **Retrieval (`GET /Files/{fileId}/download`)**: Serves the finalized file with full HTTP Range request support for playback or viewing.