Chunked upload
For large files: the content goes in chunks of 4–16 MiB, and after a connection break the upload resumes from where it stopped.
How it works
- Start the upload — POST /v1/uploads
Send the name and the size. The server checks the quota and the disk space up front and returns the upload
idand the recommended chunk sizechunkSize. - Send chunks — PATCH /v1/uploads/{id}
The request body is the chunk bytes; the
Upload-Offsetheader says which byte it starts at. - Complete — POST /v1/uploads/{id}/complete
The server checks the hash, processes the image if asked to, and creates the file.
POST /v1/uploads
{
"name": "video.mp4",
"size": 734003200,
"folder": "media",
"sha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
}| Field | Description |
|---|---|
name |
Required. File name. |
size |
Required. Size in bytes. |
folder |
Folder name. Checked at once, not after the upload: 422 when it does not exist. |
sha256 |
Content hash, 64 hex characters. Turns on deduplication and the integrity check. |
format, quality, maxWidth, maxHeight |
Image processing, as in POST /v1/files. |
Returns 201:
{
"id": "9c10a01c-a293-438c-88ba-b48181b9cd67",
"name": "video.mp4",
"size": 734003200,
"offset": 0,
"chunkSize": 8388608,
"expiresAt": "2026-11-08T17:10:44+00:00",
"file": null
}Deduplication by sha256
When sha256 is sent and the bucket already holds this content, nothing needs uploading: the
file is created at once, and the answer has chunkSize: 0 and a ready file with
deduplicated: true.
Hash and image processing
The hash is computed from the source bytes, while with processing options the result is what
gets stored. So with format, quality, maxWidth or maxHeight the hash lookup is skipped:
the upload goes the usual way, and the hash is checked on completion.
PATCH /v1/uploads/{id}
Appends a chunk. The body is the bytes; Upload-Offset is the offset the chunk starts at.
curl -X PATCH -H "Authorization: Bearer <token>" \
-H "Upload-Offset: 0" \
-H "Content-Type: application/offset+octet-stream" \
--data-binary @part-0 \
http://<host>:9090/v1/uploads/<id>Returns the upload with the new offset.
- A chunk is 4 to 16 MiB. Only the last one may be smaller.
Upload-Offsetdoes not match what has already arrived —409. Find the real offset withGETand continue from it.- The chunk goes past the declared
size—413.
GET /v1/uploads/{id}
The state of the upload: how many bytes have arrived (offset). Resuming after a break starts
with this request.
POST /v1/uploads/{id}/complete
Completes the upload and creates the file. Returns 201 and the file.
- Not everything has arrived —
409with the number of bytes received. - The hash does not match
sha256—422. - Any completion error deletes the upload — a hash mismatch, the quota ran out (
413), the folder was deleted, the image cannot be processed (422,501): the upload starts over. The exception is409“not everything arrived”: the upload stays and can be finished.
DELETE /v1/uploads/{id}
Cancels the upload and deletes what arrived. Returns 204.
Abandoned uploads
An unfinished upload expires 24 hours after its last accepted chunk (expiresAt; every chunk
extends the term), and the sweeper deletes it within an hour. Cancel by hand
only to free the space at once.