Skip to main content

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:

BucketPurposeDefault Access
publicPublic assets (logos, icons)Public
imagesUser-uploaded imagesAuthenticated
documentsPDFs and documentsAuthenticated
avatarsUser profile picturesPublic

Create a Bucket

  1. Go to Project SettingsStorage
  2. Click New Bucket
  3. 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

OptionValuesDescription
widthNumberTarget width in pixels
heightNumberTarget height in pixels
resizecover, contain, fillResize mode
quality1-100JPEG/WebP quality
formatorigin, webpOutput 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.