Resumable uploads (TUS)
For uploading large files or handling unstable network conditions, Rebase implements the TUS v1.0.0 open protocol including the Creation and Termination extensions.
Its limits are the source’s, as for every upload — see
How large a file may be.
Why large files go this way
Section titled “Why large files go this way”POST /api/storage/upload is one multipart body, which the server parses whole
before it stores it: three to five times the file in its memory (about 340 MB
for a 100 MB file). A resumable upload holds one chunk at a time — each PATCH
is under the global body limit, 10 MB by default — and is written to the store
from disk: about 50 MB for that file, and 80 MB for a 400 MB one. So size the
source’s limit to the server’s memory if clients use POST /api/storage/upload
for large files, and not otherwise.
Since 0.25 client.storage.putObject() and the admin panel send a file over
8 MiB through the resumable endpoint, in 8 MiB chunks, and resume a chunk that
fails on the way from the offset the server holds. The file lands as
POST /api/storage/upload would store it — the same key, bucket, type, custom
metadata, limits, storageAuthorize check and finalize trigger — and the
call returns the same { key, bucket, storageUrl }. When the resumable route
cannot carry it — a server without the endpoint, a chunk over the server’s body
limit, or an instance that does not hold the upload (see
Running several instances) — the
file goes through POST /api/storage/upload instead, and that source sends
every later file that way too.
Client Rebase Server │ │ │─── POST /api/storage/tus (Upload-Length: 50000000) ──────>│ (Generates session ID) │<── 201 Created (Location: /api/storage/tus/uuid-abc) ────│ │ │ │─── PATCH /api/storage/tus/uuid-abc (Upload-Offset: 0) ───>│ (Appends chunk via open/write) │<── 204 No Content (Upload-Offset: 1500000) ───────────────│ │ │ │─── PATCH /api/storage/tus/uuid-abc (Upload-Offset: 1.5M) ─>│ (Upload finishes) │<── 204 No Content (Upload-Offset: 50000000) ──────────────│ (Copies to storage, unlinks temp)Upload lifecycle
Section titled “Upload lifecycle”- Session Initialisation (
POST): The client sends the total file size in theUpload-Lengthheader and base64 metadata viaUpload-Metadata:key(orfilename),filetype,bucket,storageId,collectionandproperty, asPOST /api/storage/uploadtakes them as form fields, and Since 0.25metadata_<name>entries, which become the object’s custom metadata asmetadata_<name>fields do. The server creates an empty placeholder file under a hidden temporary directory.tus-uploads/, Since 0.25 with a record of the upload beside it (<id>.json: its key, bucket, source, type, size and owner), and returns the upload URL. Since 0.25 It also answersRebase-Upload-Result: on-complete: this server reports what it stored when the upload finishes. The upload lives in that directory, not in the process: a restarted server resumes it, and every instance that sharesSTORAGE_PATHserves it — see Running several instances. - Progress Inquiries (
HEAD): If an upload is interrupted, the client queries the upload URL using aHEADrequest. The server returns the current byte position in theUpload-Offsetheader. - Data Appending (
PATCH): The client resumes sending binary data starting at the returned offset withContent-Type: application/offset+octet-stream. The server writes each chunk to the temporary file at the byte offset the request declares, which must be the offset the server holds (409otherwise). One chunk is received at a time: aPATCHthat arrives while another for the same upload is still being received — typically a client re-sending a chunk that stalled — is answered423 UPLOAD_LOCKED, and the client resumes from the offsetHEADreports. - Finalisation: When the accumulated
Upload-Offsetmatches the declaredUpload-Length, Rebase reads the completed temporary file, wraps it as a standard JavaScriptFileobject, and saves it to the configured storage backend (local disk or S3). The temporary file is then deleted. Since 0.25 That lastPATCHanswersRebase-Upload-Resultwith the stored object —{ key, bucket, storageUrl }, whatPOST /api/storage/uploadanswers underdata— as URI-encoded JSON. A frontend on another origin can read it, andLocationandUpload-Offset, only when the server exposes them inAccess-Control-Expose-Headers: the Rebase runtime and the ejected backend do; a custom server has to. - Periodic Sweep: A background cleaner runs every 60 seconds to delete incomplete temporary uploads that began more than 24 hours ago — those another process started too, read from the directory.
Who can reach an upload
Section titled “Who can reach an upload”An upload started by a signed-in caller answers only to that caller and to administrators;
anyone else gets 404, as if the id did not exist. An upload started without a session has no
owner, so its id is a capability: whoever holds the upload URL can read its progress, append
to it and cancel it. Keep that URL secret, as you would a download token. Only a deployment
that runs storage with requireAuth: false can start one: with requireAuth on, the
default, a POST without a session is refused with 401.
Related
Section titled “Related”- Storage Configuration — sources, limits, endpoints and authorization
- Running several instances — where an upload in progress lives
- Storage SDK —
putObject(), which uses this endpoint for files over 8 MiB