API · s5w

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.

Path /v1/uploadsAccess FULLChunk 4–16 MiBLives 24 h after the last chunk

How it works

  1. 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 id and the recommended chunk size chunkSize.

  2. Send chunks — PATCH /v1/uploads/{id}

    The request body is the chunk bytes; the Upload-Offset header says which byte it starts at.

  3. Complete — POST /v1/uploads/{id}/complete

    The server checks the hash, processes the image if asked to, and creates the file.

POST /v1/uploads

json
{
  "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:

json
{
  "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.

bash
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-Offset does not match what has already arrived — 409. Find the real offset with GET and 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 — 409 with 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 is 409 “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.