# File Handling in PocketBase
## Overview
PocketBase provides comprehensive file handling capabilities:
- Single and multi-file uploads
- Automatic image thumbnail generation
- File type restrictions
- Size limits
- Public and private file access
- CDN integration support
- Image resizing and optimization
## File Fields
Add file fields to collections via the Admin UI or API:
```json
{
"name": "avatar",
"type": "file",
"options": {
"maxSelect": 1,
"maxSize": 10485760,
"mimeTypes": ["image/*"],
"thumbs": ["100x100", "300x300"]
}
}
```
### File Field Options
#### maxSelect
Maximum number of files allowed:
- `1` - Single file upload
- `null` or `2+` - Multiple files
```json
"maxSelect": 5 // Allow up to 5 files
```
#### maxSize
Maximum file size in bytes:
```json
"maxSize": 10485760 // 10MB
// Common sizes:
5MB = 5242880
10MB = 10485760
50MB = 52428800
100MB = 104857600
```
#### mimeTypes
Allowed MIME types (array):
```json
// Images only
"mimeTypes": ["image/jpeg", "image/png", "image/gif"]
// Images and videos
"mimeTypes": ["image/*", "video/*"]
// Any file type
"mimeTypes": ["*"]
// Specific types
"mimeTypes": [
"image/jpeg",
"image/png",
"application/pdf",
"text/csv"
]
```
#### thumbs
Auto-generate image thumbnails:
```json
"thumbs": [
"100x100", // Small square
"300x300", // Medium square
"800x600", // Large thumbnail
"1200x800" // Extra large
]
// Formats:
// WIDTHxHEIGHT - exact size, may crop
// WIDTHx - width only, maintain aspect ratio
// xHEIGHT - height only, maintain aspect ratio
```
## Uploading Files
### Single File Upload
```javascript
const formData = new FormData();
formData.append('avatar', fileInput.files[0]);
const user = await pb.collection('users').update('USER_ID', formData);
// Access file URL
const avatarUrl = pb.files.getURL(user, user.avatar);
console.log(avatarUrl);
```
### Multiple File Upload
```javascript
const formData = new FormData();
// Add multiple files
formData.append('images', fileInput.files[0]);
formData.append('images', fileInput.files[1]);
formData.append('images', fileInput.files[2]);
const post = await pb.collection('posts').update('POST_ID', formData);
// Access all files
post.images.forEach(image => {
const url = pb.files.getURL(post, image);
console.log(url);
});
```
### Upload with Metadata
```javascript
const formData = new FormData();
formData.append('document', fileInput.files[0], {
filename: 'custom-name.pdf', // Custom filename
type: 'application/pdf',
lastModified: Date.now()
});
const record = await pb.collection('documents').update('DOC_ID', formData);
```
## File URLs
### Get File URL
```javascript
// Basic URL
const url = pb.files.getURL(record, record.avatar);
// With thumbnail
const thumbnailUrl = pb.files.getURL(
record,
record.avatar,
{ thumb: '300x300' }
);
// Custom options
const url = pb.files.getURL(
record,
record.avatar,
{
thumb: '100x100',
expires: 3600 // URL expires in 1 hour (for private files)
}
);
```
### URL Parameters
**For public files:**
```javascript
// Direct access (public files only)
const url = pb.files.getURL(record, record.avatar);
// Returns: http://localhost:8090/api/files/COLLECTION_ID/RECORD_ID/filename.jpg
```
**For private files:**
```javascript
// Temporary signed URL (1 hour expiry)
const url = pb.files.getURL(record, record.avatar, { expires: 3600 });
// Returns: http://localhost:8090/api/files/COLLECTION_ID/RECORD_ID/filename.jpg?token=SIGNED_TOKEN
```
**Thumbnail URLs:**
```javascript
// Automatic thumbnail
const thumbUrl = pb.files.getURL(record, record.avatar, {
thumb: '300x300'
});
// Returns: thumbnail if available
```
## File Access Control
### Public Files
Default behavior - anyone with URL can access:
```javascript
// File is publicly accessible
const url = pb.files.getURL(record, record.avatar);
// Can be shared and accessed by anyone
```
### Private Files
Restrict access to authenticated users:
**1. Configure in Admin UI**
- Go to Collection → File field options
- Enable "Private files"
- Set file rules (e.g., `user_id = @request.auth.id`)
**2. Use signed URLs**
```javascript
// Generate signed URL (expires)
const signedUrl = pb.files.getURL(record, record.avatar, {
expires: 3600 // Expires in 1 hour
});
// Use signed URL in frontend
```
**3. Access files with auth token**
```javascript
// Include auth token in requests
const response = await fetch(signedUrl, {
headers: {
'Authorization': `Bearer ${pb.authStore.token}`
}
});
```
### File Rules
Control who can upload/view/delete files:
```javascript
// Owner can only access their files
File Rule: user_id = @request.auth.id
// Public read, authenticated write
List Rule: true
View Rule: true
Create Rule: @request.auth.id != ""
Update Rule: user_id = @request.auth.id
Delete Rule: user_id = @request.auth.id
// Admins only
Create Rule: @request.auth.role = "admin"
Update Rule: @request.auth.role = "admin"
Delete Rule: @request.auth.role = "admin"
```
## Download Files
### Browser Download
```javascript
// Download via browser
const link = document.createElement('a');
link.href = pb.files.getURL(record, record.document);
link.download = record.document;
link.click();
```
### Programmatic Download
```javascript
// Fetch file as blob
const blob = await pb.files.download(record, record.document);
// Or with fetch
const response = await fetch(pb.files.getURL(record, record.document));
const blob = await response.blob();
// Save file
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = record.document;
a.click();
```
## Deleting Files
### Delete Single File
```javascript
// Remove file from record
const updated = await pb.collection('users').update('USER_ID', {
avatar: null // Remove avatar
});
```
### Delete Multiple Files
```javascript
// Remove specific files from array
const updated = await pb.collection('posts').update('POST_ID', {
images: record.images.filter(img => img !== imageToRemove)
});
```
### Delete File on Record Delete
Files are automatically deleted when record is deleted:
```javascript
await pb.collection('posts').delete('POST_ID');
// All associated files are removed automatically
```
## Image Thumbnails
### Automatic Thumbnails
Define in file field options:
```json
{
"name": "images",
"type": "file",
"options": {
"maxSelect": 10,
"maxSize": 10485760,
"mimeTypes": ["image/*"],
"thumbs": ["100x100", "300x300", "800x600"]
}
}
```
### Access Thumbnails
```javascript
// Get specific thumbnail size
const smallThumb = pb.files.getURL(post, post.images[0], {
thumb: '100x100'
});
const mediumThumb = pb.files.getURL(post, post.images[0], {
thumb: '300x300'
});
// Auto-select best thumbnail
const thumb = pb.files.getURL(post, post.images[0], {
thumb: '300x300' // Returns thumbnail or original if not available
});
```
### Thumbnail Formats
- `WxH` - Crop to exact dimensions
- `Wx` - Width only, maintain aspect ratio
- `xH` - Height only, maintain aspect ratio
- `Wx0` - Width, no height limit
- `0xH` - Height, no width limit
## Frontend Integration
### React Image Component
```javascript
import { useState } from 'react';
function ImageUpload() {
const [file, setFile] = useState(null);
const [uploadedUrl, setUploadedUrl] = useState('');
const handleUpload = async (e) => {
const file = e.target.files[0];
if (!file) return;
const formData = new FormData();
formData.append('avatar', file);
const updated = await pb.collection('users').update('USER_ID', formData);
setUploadedUrl(pb.files.getURL(updated, updated.avatar));
};
return (