Storage
Store and serve files like images, documents, and media in your INSTROC application.
Overview
INSTROC storage provides:
- File uploads with automatic optimization
- Organized buckets for different file types
- Access control policies
- CDN delivery for fast loading
Buckets
Buckets organize your files by purpose:
| Bucket | Purpose | Default Access |
|---|---|---|
public | Public assets (logos, icons) | Public |
images | User-uploaded images | Authenticated |
documents | PDFs and documents | Authenticated |
avatars | User profile pictures | Public |
Create a Bucket
- Go to Project Settings → Storage
- Click New Bucket
- Set name and access policy
Or via SQL:
INSERT INTO storage.buckets (id, name, public)
VALUES ('my-bucket', 'my-bucket', true);
Uploading Files
Basic Upload
import { storage } from '@/lib/backend';
const file = event.target.files[0];
const { data, error } = await storage
.from('images')
.upload(`uploads/${file.name}`, file);
if (data) {
console.log('Uploaded:', data.path);
}
With Options
const { data, error } = await storage
.from('images')
.upload(`uploads/${file.name}`, file, {
cacheControl: '3600',
upsert: true, // Overwrite if exists
contentType: 'image/jpeg'
});
Upload Component Example
function ImageUpload({ onUpload }) {
const [uploading, setUploading] = useState(false);
const handleUpload = async (event) => {
const file = event.target.files[0];
if (!file) return;
setUploading(true);
const filename = `${Date.now()}-${file.name}`;
const { data, error } = await storage
.from('images')
.upload(filename, file);
setUploading(false);
if (error) {
alert('Upload failed');
} else {
const { data: { publicUrl } } = storage
.from('images')
.getPublicUrl(data.path);
onUpload(publicUrl);
}
};
return (
<input
type="file"
accept="image/*"
onChange={handleUpload}
disabled={uploading}
/>
);
}
Getting File URLs
Public Files
const { data } = storage
.from('public')
.getPublicUrl('logo.png');
console.log(data.publicUrl);
// https://xxx.supabase.co/storage/v1/object/public/public/logo.png
Protected Files
For private files, create a signed URL:
const { data, error } = await storage
.from('documents')
.createSignedUrl('private/report.pdf', 3600); // 1 hour
console.log(data.signedUrl);
Downloading Files
const { data, error } = await storage
.from('documents')
.download('report.pdf');
if (data) {
// data is a Blob
const url = URL.createObjectURL(data);
window.open(url);
}
Listing Files
const { data, error } = await storage
.from('images')
.list('uploads', {
limit: 100,
offset: 0,
sortBy: { column: 'created_at', order: 'desc' }
});
// Returns array of file metadata
data.forEach(file => {
console.log(file.name, file.metadata.size);
});
Deleting Files
Single File
const { error } = await storage
.from('images')
.remove(['uploads/old-image.jpg']);
Multiple Files
const { error } = await storage
.from('images')
.remove([
'uploads/image1.jpg',
'uploads/image2.jpg',
'uploads/image3.jpg'
]);
Moving and Copying
Move File
const { error } = await storage
.from('images')
.move('uploads/temp.jpg', 'uploads/permanent.jpg');
Copy File
const { error } = await storage
.from('images')
.copy('uploads/original.jpg', 'uploads/backup.jpg');
Access Control
Bucket Policies
Control who can access files:
Public read access:
CREATE POLICY "Public can view"
ON storage.objects FOR SELECT
USING (bucket_id = 'public');
Authenticated users only:
CREATE POLICY "Auth users can view"
ON storage.objects FOR SELECT
USING (
bucket_id = 'images' AND
auth.role() = 'authenticated'
);
Users can only access their files:
CREATE POLICY "Users access own files"
ON storage.objects FOR SELECT
USING (
bucket_id = 'documents' AND
(storage.foldername(name))[1] = auth.uid()::text
);
Image Transformations
Transform images on the fly:
const { data } = storage
.from('images')
.getPublicUrl('photo.jpg', {
transform: {
width: 300,
height: 200,
resize: 'cover'
}
});
Available Transformations
| Option | Values | Description |
|---|---|---|
width | Number | Target width in pixels |
height | Number | Target height in pixels |
resize | cover, contain, fill | Resize mode |
quality | 1-100 | JPEG/WebP quality |
format | origin, webp | Output format |
Best Practices
Organize with Folders
Use consistent folder structure:
images/
├── users/{user_id}/
│ ├── avatar.jpg
│ └── uploads/
├── products/{product_id}/
└── blog/{post_id}/
Generate Unique Names
Avoid conflicts with unique filenames:
const filename = `${Date.now()}-${Math.random().toString(36).slice(2)}`;
Validate Uploads
Check file types and sizes:
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
if (file.size > MAX_SIZE) {
throw new Error('File too large');
}
if (!ALLOWED_TYPES.includes(file.type)) {
throw new Error('Invalid file type');
}
Clean Up Unused Files
Periodically remove orphaned files to save storage.