Working with files
Paths and folders, uploading (and replacing), large files with resumable uploads, listing, moving and renaming, downloading and deleting, and the limits that apply.
Paths and folders
A file is addressed by its bucket and a path: avatars + 6e19…/photo.png. Folders are just the parts of the path before the last /; they exist as long as something is in them (New folder in the console creates an empty placeholder). Use the user's id as the first folder for per-user files — the own folder rules check exactly that (Access, links and delivery).
Keep names URL-safe (letters, digits, -, _, ., /); the SDKs encode anything else.
Upload
const user = await sovaria.auth.me()
await sovaria.storage.upload('avatars', `${user.id}/photo.png`, file, {
contentType: 'image/png',
upsert: true, // replace the file if it exists
})| Situation | Result |
|---|---|
| The path is new | The file is stored. |
The path exists, without upsert | 409 Object … already exists — nothing is overwritten. |
The path exists, with upsert: true | The file is replaced. There is no version history for files. |
| Bigger than the bucket's or plan's largest file | 413. |
| Type not in the bucket's allowed types | Refused. |
| The project's storage is full | 403 with a plan code (Free stops; paid plans bill beyond — Billing, invoices and overage). |
For browser uploads straight to Sovaria without passing through your server, create a signed upload URL on your server and let the browser PUT the file to it (Access, links and delivery).
Large files: resumable uploads
For files of tens of megabytes or more, or unreliable networks, upload in chunks and resume after a failure (a TUS-style protocol):
Start
Returns
201with aLocationheader for the upload.curl -si -X POST "$URL/api/v1/store/resumable/videos/$USER_ID/clip.mp4" \ -H "Authorization: Bearer $TOKEN" -H "Upload-Length: 734003200" # Location: /api/v1/store/resumable/<upload-id> Upload-Offset: 0Send chunks
Each
PATCHcarries the offset it starts at; a wrong offset is refused so chunks can't be appended twice.curl -s -X PATCH "$URL/api/v1/store/resumable/<upload-id>" \ -H "Authorization: Bearer $TOKEN" -H "Upload-Offset: 0" \ -H 'Content-Type: application/offset+octet-stream' --data-binary @chunk-0Resume after a failure
HEADthe upload to readUpload-Offset, then continue from there.Finish
Send the last chunk with
X-Upload-Complete: 1; the file appears in the bucket.
The S3-compatible storage access API also supports multipart uploads, so aws s3 cp, rclone and S3 libraries handle large files for you.
List, move, rename, delete
const files = await sovaria.storage.listObjects('avatars', { prefix: `${user.id}/` })
// Move or rename (both paths are bucket/path)
await sovaria.storage.moveObject({ from_path: `avatars/${user.id}/old.png`, to_path: `avatars/${user.id}/new.png` })
await sovaria.storage.remove('avatars', `${user.id}/new.png`)- Listing returns each file's path, size, content type and last change.
- A move needs
WRITEpermission on both the old and the new path. - Deleting is immediate and permanent — there is no trash. Deleting a bucket deletes every file in it.
Download
sovaria.storage.download(bucket, path) returns a Blob for the signed-in user. For <img> tags and links, use a public URL (public buckets) or a signed URL (private ones) instead of downloading in JavaScript — Access, links and delivery.
Limits
| Free | Pro | Ultra | Team | Enterprise | |
|---|---|---|---|---|---|
| Object storage | 1 GB | 100 GB | 300 GB | 1 TB | 5 TB |
| Files | 100 MB | 2 GB | 10 GB | 50 GB | 100 GB |
| Buckets | 3 | 100 | 500 | 2,000 | 10,000 |
| Monthly bandwidth | 5,368,709,120 | 107,374,182,400 | 322,122,547,200 | 1,099,511,627,776 | 5,497,558,138,880 |
A bucket can set a lower largest-file size and a list of allowed content types (Bucket › Settings, or file_size_limit / allowed_mime_types when creating it).