diff --git a/TUS-Resumable-Uploads.md b/TUS-Resumable-Uploads.md index c709e55..18d1c6a 100644 --- a/TUS-Resumable-Uploads.md +++ b/TUS-Resumable-Uploads.md @@ -6,6 +6,7 @@ SeaweedFS supports the [TUS](https://tus.io) protocol for resumable file uploads - **Resumable uploads**: Resume interrupted uploads without re-sending data - **Chunked uploads**: Upload large files in smaller pieces +- **Parallel uploads**: Upload parts of a file concurrently and concatenate them server-side - **Simple protocol**: Standard HTTP methods with custom headers - **Wide client support**: Libraries available for JavaScript, Python, Go, Java, and more @@ -68,6 +69,7 @@ weed server -filer=true -filer.tusBasePath= - `creation`: Create new upload sessions - `creation-with-upload`: Upload data in the creation request - `termination`: Cancel/delete uploads +- `concatenation`: Concatenate completed partial uploads into one file, enabling parallel uploads ## Usage Examples @@ -82,7 +84,7 @@ Response headers: ``` Tus-Resumable: 1.0.0 Tus-Version: 1.0.0 -Tus-Extension: creation,creation-with-upload,termination +Tus-Extension: creation,creation-with-upload,termination,concatenation Tus-Max-Size: 5368709120 ``` @@ -153,6 +155,51 @@ curl -X DELETE http://localhost:8888/.tus/.uploads/abc123-uuid \ -H "Tus-Resumable: 1.0.0" ``` +### 7. Parallel Upload with Concatenation + +Upload parts of one file concurrently as partial uploads, then concatenate them in order: + +```bash +# Create a partial upload per part (Upload-Length is that part's size) +curl -i -X POST http://localhost:8888/.tus/path/to/file.txt \ + -H "Tus-Resumable: 1.0.0" \ + -H "Upload-Concat: partial" \ + -H "Upload-Length: 500" +# Location: /.tus/.uploads/part1-uuid + +curl -i -X POST http://localhost:8888/.tus/path/to/file.txt \ + -H "Tus-Resumable: 1.0.0" \ + -H "Upload-Concat: partial" \ + -H "Upload-Length: 500" +# Location: /.tus/.uploads/part2-uuid + +# Upload the parts, concurrently if desired +curl -X PATCH http://localhost:8888/.tus/.uploads/part1-uuid \ + -H "Tus-Resumable: 1.0.0" \ + -H "Upload-Offset: 0" \ + -H "Content-Type: application/offset+octet-stream" \ + --data-binary @part1.bin & +curl -X PATCH http://localhost:8888/.tus/.uploads/part2-uuid \ + -H "Tus-Resumable: 1.0.0" \ + -H "Upload-Offset: 0" \ + -H "Content-Type: application/offset+octet-stream" \ + --data-binary @part2.bin & +wait + +# Concatenate the completed partials, in order, into the final file +curl -X POST http://localhost:8888/.tus/path/to/file.txt \ + -H "Tus-Resumable: 1.0.0" \ + -H "Upload-Concat: final;/.tus/.uploads/part1-uuid /.tus/.uploads/part2-uuid" +``` + +Notes: + +- A completed partial does not appear at the target path; the file is created by the final request. +- The final request must not include `Upload-Length`; the file size is the sum of the partial sizes, capped by `-tusMaxSizeMB`. +- All listed partials must be finished before concatenation; concatenating an unfinished partial fails. +- Concatenation consumes the partials, so each partial can be used only once. +- Partials that are never concatenated expire like any other session after `-tusSessionExpiry`. + ## TUS Headers ### Request Headers @@ -163,6 +210,7 @@ curl -X DELETE http://localhost:8888/.tus/.uploads/abc123-uuid \ | `Upload-Length` | POST | Total file size in bytes | | `Upload-Offset` | PATCH | Current byte offset | | `Upload-Metadata` | Optional | Base64-encoded key-value pairs | +| `Upload-Concat` | Optional | `partial` creates a partial upload; `final; ...` concatenates completed partials | | `Content-Type` | PATCH | Must be `application/offset+octet-stream` | | `Content-Length` | PATCH | Size of data being uploaded | @@ -201,6 +249,8 @@ const file = document.querySelector("input[type=file]").files[0]; const upload = new tus.Upload(file, { endpoint: "http://localhost:8888/.tus/uploads/", retryDelays: [0, 3000, 5000, 10000, 20000], + parallelUploads: 3, // uses the concatenation extension + metadata: { filename: file.name, filetype: file.type, @@ -267,9 +317,10 @@ func main() { ## Limitations -- Maximum upload size: 5GB by default, configurable with `-tusMaxSizeMB` +- Maximum upload size: 5GB by default, configurable with `-tusMaxSizeMB`; a concatenated file's total size is capped by the same limit - Session expiration: 24 hours by default, configurable with `-tusSessionExpiry`; expired sessions are removed by an hourly background cleanup -- Only single-file uploads (no concatenation extension) +- Concatenation partials are single-use: once concatenated into a final upload they are consumed and cannot be reused +- `concatenation-unfinished` is not supported: all partials must be complete before the final request ## See Also