The tus 1.0 protocol in ASP.NET Core
Most resumable uploading is a private arrangement between one vendor's JavaScript and that same vendor's server. tus is the version of that arrangement written down as an open standard — which is what makes the two halves replaceable.
Chunked upload is an implementation detail: a client slices a file, a server reassembles it, and the two agree privately on how. It works, and CoreUpload does it. What it does not give you is the ability to change your mind.
tus writes that agreement down. Because the contract is public, the client and the server stop being a matched pair: a tus client can talk to any tus server, and a tus server accepts any tus client. If you later move the browser half to something else, or move storage behind a different tus implementation, the other half does not have to be rewritten. That is the whole argument for the protocol, and it is worth more than any single feature in it.
tus begins with a capability handshake: the client sends OPTIONS and the server answers with what it supports. This is the live response from the endpoint on this site — you can run it yourself:
$ curl -i -X OPTIONS -H "Tus-Resumable: 1.0.0" https://coreupload.com/api/upload/tus
HTTP/1.1 204 No Content
Tus-Resumable: 1.0.0
Tus-Version: 1.0.0
Tus-Extension: creation,termination,checksum
Tus-Checksum-Algorithm: sha1,sha256
Tus-Max-Size: 5368709120Three extensions are implemented — creation (start an upload), termination (abandon one and free the space), and checksum (verify each chunk in flight). Tus-Max-Size is 5 GiB by default.
| Request | Purpose |
|---|---|
OPTIONS /tus | Capability discovery — the handshake above. |
POST /tus | Create an upload from Upload-Length and Upload-Metadata; returns the session URL in Location. |
HEAD /tus/{id} | Ask how many bytes the server already holds. This is the question that makes resuming possible. |
PATCH /tus/{id} | Send bytes from Upload-Offset, as application/offset+octet-stream. |
DELETE /tus/{id} | Terminate the session and release the partial file. |
The flow after an interruption is simply HEAD then PATCH: ask the server where it got to, then continue from there. Nothing is re-sent, and the client does not have to have been the one that started the upload.
The endpoints are mapped by MapCoreUploadEndpoints() like every other strategy, so on the server there is nothing extra to register. On the page it is one attribute:
<core-upload asp-upload-url="/api/upload/upload"
asp-strategy="tus"
asp-chunk-size="5242880"
asp-persist-state="true"
asp-persist-adapter="indexeddb"></core-upload>Point asp-tus-endpoint elsewhere if your tus server is not this application. Pair it with asp-persist-adapter="indexeddb" so the session URL survives a closed tab — the protocol can resume, but only if the client remembers where to resume to.
To verify chunks in flight, enable the checksum extension. It is off by default:
CoreUpload.create('#uploader', { strategy: 'tus', tusChecksum: 'sha256' });Each PATCH then carries an Upload-Checksum header and the server rejects a chunk that does not match, rather than storing corruption and discovering it later.
Both survive a dropped connection, so the choice is about what else you need.
- Choose tus when the upload has to survive the browser closing, when another system may need to talk to the same endpoint, or when you want the freedom to replace either half later.
- Choose chunked when both halves are yours and always will be. It has fewer round trips, since there is no handshake and no per-session create.
- Choose direct-to-cloud when the destination is S3, Azure or GCS and you would rather your server never carried the bytes.