File storage
A bucket for uploads, images and downloads. Browsers can upload straight to it.
File storage holds files of any size. Your code reads and writes them by key (like a path: uploads/cake.jpg), and visitors' browsers can upload and download directly, without going through your code. The first bucket is called FILES unless you name it.
Good for: image uploads, documents, backups. Billed: per upload or download, and by how much is stored.
From your app's code
await env.FILES.put('avatars/ada.png', imageBytes)
const file = await env.FILES.get('avatars/ada.png')Uploads from a browser
Your server asks for a short-lived upload link, and the browser sends the file straight to storage:
// On your server:
const { url } = await ocp.app(appId).storage('FILES').signedUrl('uploads/photo.jpg', { method: 'PUT' })
// In the browser:
await fetch(url, { method: 'PUT', body: file })Links last 15 minutes by default, and up to 7 days. The same works for downloads (GET) and deletes (DELETE).
From the dashboard
Open the bucket and choose the Files tab. You can browse folders, see pictures as thumbnails, preview pictures, video, audio, PDFs, text, Markdown, CSV and JSON, upload files or whole folders (drag them in, or use Upload), rename and move files and folders, and delete several at once.
The dashboard keeps two kinds of small files of its own in your bucket, which your code will see if it lists everything:
<folder>/.keep: an empty file that keeps a folder made in the dashboard until something is put in it. Buckets don't have real folders, only file names with/in them..thumbs/<file>.webp: a small preview of each picture uploaded from the dashboard, so the grid loads quickly. Pictures your code saves don't get one, and the dashboard shows the picture itself instead.
Both are hidden in the dashboard and are moved and deleted along with their files. Renaming or moving a file copies it to the new name and then deletes the old one, so it counts as a copy and a delete.
From your own scripts
const files = ocp.app(appId).storage('FILES')
await files.put('backups/2026-10-21.sql', dump, { contentType: 'application/sql' })
const res = await files.get('backups/2026-10-21.sql') // a Response, or null
const { data } = await files.list({ prefix: 'backups/' })The SDK asks once for temporary credentials that reach only this bucket, then talks to it directly. You can use those credentials with any S3 tool too: see credentials.
Removing a bucket deletes every file in it.
opencloudport