Client Server
| |
|--- POST (partial, part1) -------------------->| 201 + Location /tus/{id1}
|--- POST (partial, part2) -------------------->| 201 + Location /tus/{id2}
|--- POST (partial, part3) -------------------->| 201 + Location /tus/{id3}
| |
|--- PATCH /tus/{id1} (bytes 0-N) ------------>| 204 (concurrent)
|--- PATCH /tus/{id2} (bytes 0-N) ------------>| 204 (concurrent)
|--- PATCH /tus/{id3} (bytes 0-N) ------------>| 204 (concurrent)
| |
|--- POST (final; /tus/{id1} /tus/{id2} ...) ->| 201 + Location /tus/{finalId}
| |
|--- HEAD /tus/{finalId} --------------------->| 200 (merged, complete)
How It Works
-
Create partial uploads — one POST per part, each with
Upload-Concat: partial -
Upload data in parallel — each part can be uploaded concurrently via PATCH
-
Merge — a single POST with
Upload-Concat: final; /tus/{id1} /tus/{id2} /tus/{id3}merges all parts
Each partial may be referenced only once, and a single merge may reference at most quarkus.tus.max-concat-parts (default 1000) of them. Both violations are rejected with 400.
Step-by-Step with curl
1. Create Partial Uploads
# Part 1 (first 1MB)
curl -X POST http://localhost:8080/tus \
-H "Tus-Resumable: 1.0.0" \
-H "Upload-Length: 1048576" \
-H "Upload-Concat: partial" \
-i
# -> Location: /tus/aaa-...
# Part 2 (second 1MB)
curl -X POST http://localhost:8080/tus \
-H "Tus-Resumable: 1.0.0" \
-H "Upload-Length: 1048576" \
-H "Upload-Concat: partial" \
-i
# -> Location: /tus/bbb-...
# Part 3 (final 500KB)
curl -X POST http://localhost:8080/tus \
-H "Tus-Resumable: 1.0.0" \
-H "Upload-Length: 512000" \
-H "Upload-Concat: partial" \
-i
# -> Location: /tus/ccc-...
2. Upload Data in Parallel
Run these in separate terminals or as background processes:
# Upload part 1
curl -X PATCH http://localhost:8080/tus/aaa-... \
-H "Tus-Resumable: 1.0.0" \
-H "Upload-Offset: 0" \
-H "Content-Type: application/offset+octet-stream" \
--data-binary @part1.bin &
# Upload part 2
curl -X PATCH http://localhost:8080/tus/bbb-... \
-H "Tus-Resumable: 1.0.0" \
-H "Upload-Offset: 0" \
-H "Content-Type: application/offset+octet-stream" \
--data-binary @part2.bin &
# Upload part 3
curl -X PATCH http://localhost:8080/tus/ccc-... \
-H "Tus-Resumable: 1.0.0" \
-H "Upload-Offset: 0" \
-H "Content-Type: application/offset+octet-stream" \
--data-binary @part3.bin &
wait # Wait for all uploads to complete
JavaScript Client Example
Using the official tus-js-client with parallel uploads:
import * as tus from "tus-js-client";
const file = document.getElementById("fileInput").files[0];
const upload = new tus.Upload(file, {
endpoint: "http://localhost:8080/tus",
chunkSize: 5 * 1024 * 1024, // 5MB chunks
parallelUploads: 3, // Upload 3 parts concurrently
retryDelays: [0, 1000, 3000],
metadata: {
filename: file.name,
filetype: file.type,
},
onProgress: (bytesUploaded, bytesTotal) => {
const pct = ((bytesUploaded / bytesTotal) * 100).toFixed(1);
console.log(`${pct}% uploaded`);
},
onSuccess: () => {
console.log("Upload complete:", upload.url);
},
onError: (error) => {
console.error("Upload failed:", error);
},
});
upload.start();
When parallelUploads > 1, tus-js-client automatically:
-
Splits the file into parts
-
Creates partial uploads for each
-
Uploads parts concurrently
-
Sends the final concatenation request
Unfinished Concatenation
If some partial uploads are still in progress when you send the final concatenation request, the server supports concatenation-unfinished: it creates a pending final upload and auto-merges when all partials complete. Check progress via HEAD on the final upload ID.
Performance Tips
-
Part count: 3-5 parts is typically optimal. More parts add overhead; fewer parts limit parallelism.
-
Part sizing: Equal-sized parts maximize throughput. Make the last part smaller if the file size isn’t evenly divisible.
-
Chunk size: Each partial upload can itself use chunked PATCH requests for resumability. Set
quarkus.tus.max-chunk-sizeappropriately. -
Network: Parallel uploads help most on high-bandwidth, high-latency connections where a single TCP stream can’t saturate the link.
-
Server-side merge: The merge operation copies data sequentially. For very large uploads (> 1GB), ensure sufficient disk I/O capacity and temporary storage.