> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/pocketbase/pocketbase/llms.txt
> Use this file to discover all available pages before exploring further.

# Files

> File upload, storage, and management in PocketBase

PocketBase provides built-in file management capabilities with support for local and S3-compatible storage, automatic file validation, and thumbnail generation for images.

## File fields

File storage is handled through file fields in your collections:

```go theme={null}
collection.Fields.Add(&core.FileField{
    Name:      "attachments",
    MaxSelect: 5,              // Allow up to 5 files
    MaxSize:   5242880,        // 5MB per file
    MimeTypes: []string{       // Restrict file types
        "image/jpeg",
        "image/png",
        "application/pdf",
    },
    Thumbs: []string{          // Image thumbnails
        "100x100",
        "300x300",
        "0x500",  // Width auto, height 500px
    },
    Protected: false,          // Public access
    Required:  false,
})
```

## File field options

### MaxSelect

Controls whether the field stores a single file or multiple files:

```go theme={null}
// Single file
field.MaxSelect = 1  // or 0
// Value type: string (filename)

// Multiple files
field.MaxSelect = 5
// Value type: []string (array of filenames)
```

### MaxSize

Specifies the maximum file size in bytes:

```go theme={null}
field.MaxSize = 5 << 20  // 5MB
field.MaxSize = 10485760 // 10MB
```

<Note>
  If not set, the default maximum size is **5MB** per file.
</Note>

### MimeTypes

Restricts allowed file types:

```go theme={null}
field.MimeTypes = []string{
    "image/jpeg",
    "image/png",
    "image/gif",
    "image/webp",
    "video/mp4",
    "application/pdf",
    "application/zip",
}
```

Leave empty to allow all file types.

### Thumbs

Defines thumbnail sizes for image files:

```go theme={null}
field.Thumbs = []string{
    "100x100",   // Crop to 100x100 from center
    "100x100t",  // Crop to 100x100 from top
    "100x100b",  // Crop to 100x100 from bottom
    "100x100f",  // Fit inside 100x100 (no cropping)
    "0x300",     // Height 300px, width auto
    "200x0",     // Width 200px, height auto
}
```

Thumbnails are generated automatically for supported image formats.

### Protected

Controls file access:

```go theme={null}
// Public files (default)
field.Protected = false
// URL: /api/files/{collection}/{record}/{filename}

// Protected files
field.Protected = true
// URL: /api/files/{collection}/{record}/{filename}?token={fileToken}
```

<Info>
  Even public files have randomized filenames (e.g., `document_Ab3Kf9g2.pdf`), providing obscurity. Protected files require a short-lived token for access.
</Info>

## Uploading files

### Single file upload

```go theme={null}
file := &filesystem.File{
    Name:   "document.pdf",
    Reader: fileReader,  // io.Reader
}

record.Set("attachment", file)
app.Save(record)  // File is uploaded during save
```

### Multiple file upload

```go theme={null}
files := []*filesystem.File{
    {Name: "image1.jpg", Reader: reader1},
    {Name: "image2.jpg", Reader: reader2},
    {Name: "image3.jpg", Reader: reader3},
}

record.Set("gallery", files)
app.Save(record)
```

### Modifying uploaded files

```go theme={null}
// Get files pending upload
unsavedFiles := record.GetUnsavedFiles("documents")

// Modify before saving
for _, file := range unsavedFiles {
    // Add prefix to filename
    file.Name = "doc_" + file.Name
    
    // Wrap reader for processing
    file.Reader = newProcessingReader(file.Reader)
}

app.Save(record)
```

### File modifiers

File fields support special modifier operations:

```go theme={null}
// Append files
record.Set("images+", newFile)
record.Set("images+", []*filesystem.File{file1, file2})

// Prepend files
record.Set("+images", newFile)

// Remove files by name
record.Set("images-", "old_image.jpg")
record.Set("images-", []string{"file1.jpg", "file2.jpg"})
```

## File storage

### Storage structure

Files are stored in the following hierarchy:

```
storage/
  └── {collectionId}/
      └── {recordId}/
          ├── file1_Ab3Kf9g2.jpg
          ├── file2_Xk8Mn4p1.pdf
          └── thumbs_file1_Ab3Kf9g2.jpg/
              ├── 100x100_file1_Ab3Kf9g2.jpg
              └── 300x300_file1_Ab3Kf9g2.jpg
```

### Filename normalization

PocketBase automatically adds a random suffix to filenames:

```
Original:  document.pdf
Stored as: document_Ab3Kf9g2.pdf
```

This prevents:

* Filename conflicts
* Guessing file URLs
* Accidental overwrites

## Accessing files

### File URLs

```go theme={null}
// Get filename
filename := record.GetString("avatar")

// Build URL
url := fmt.Sprintf(
    "%s/api/files/%s/%s/%s",
    app.Settings().Meta.AppUrl,
    record.Collection().Id,
    record.Id,
    filename,
)
```

### Public file URLs

For non-protected files:

```
GET /api/files/{collectionId}/{recordId}/{filename}
```

Example:

```
https://example.com/api/files/posts_collection/record123/image_Ab3Kf9g2.jpg
```

### Protected file URLs

For protected files, generate a file token:

```go theme={null}
// Generate file token (valid for 3 minutes by default)
token, err := core.GenerateFileToken(
    app,
    record,
    filename,
)

// Build URL with token
url := fmt.Sprintf(
    "%s/api/files/%s/%s/%s?token=%s",
    app.Settings().Meta.AppUrl,
    record.Collection().Id,
    record.Id,
    filename,
    token,
)
```

### Thumbnail URLs

Access image thumbnails:

```
GET /api/files/{collectionId}/{recordId}/thumbs_{filename}/{size}_{filename}
```

Example:

```
https://example.com/api/files/posts/record123/thumbs_image_Ab3Kf9g2.jpg/100x100_image_Ab3Kf9g2.jpg
```

## File validation

### Automatic validation

File fields automatically validate:

* File count (MaxSelect)
* File size (MaxSize)
* MIME type (MimeTypes)
* Filename format

### Custom validation

```go theme={null}
app.OnRecordValidate().Bind(&hook.Handler[*core.RecordEvent]{
    Func: func(e *core.RecordEvent) error {
        if e.Record.Collection().Name == "posts" {
            files := e.Record.GetUnsavedFiles("images")
            
            for _, file := range files {
                // Custom validation logic
                if file.Size > 2<<20 {
                    return validation.NewError(
                        "validation_file_too_large",
                        "Images must be under 2MB",
                    )
                }
                
                // Scan for malware, check dimensions, etc.
            }
        }
        return e.Next()
    },
})
```

## File operations

### Listing files

```go theme={null}
// Get all filenames for a field
filenames := record.GetStringSlice("attachments")

for _, filename := range filenames {
    fmt.Println(filename)
}
```

### Finding files

```go theme={null}
// Find which field contains a file
field := record.FindFileFieldByFile("document_Ab3Kf9g2.pdf")

if field != nil {
    fmt.Println("File is in field:", field.Name)
}
```

### Deleting files

Files are automatically deleted when:

1. Record is deleted
2. File field value is updated (old files removed)
3. File is explicitly removed using modifiers

```go theme={null}
// Remove specific file
record.Set("images-", "old_image.jpg")
app.Save(record)  // File deleted after save

// Replace all files
record.Set("images", newFiles)
app.Save(record)  // Old files deleted

// Delete record
app.Delete(record)  // All files deleted
```

## S3 storage

PocketBase supports S3-compatible storage (AWS S3, MinIO, DigitalOcean Spaces, etc.):

```go theme={null}
app.Settings().S3 = settings.S3Config{
    Enabled:        true,
    Bucket:         "my-bucket",
    Region:         "us-east-1",
    Endpoint:       "s3.amazonaws.com",
    AccessKey:      "ACCESS_KEY",
    Secret:         "SECRET_KEY",
    ForcePathStyle: false,
}

app.SaveSettings()
```

<Warning>
  When switching from local to S3 storage, you must manually migrate existing files. New files will automatically use S3.
</Warning>

## File hooks

### Upload processing

```go theme={null}
app.OnRecordCreateExecute().Bind(&hook.Handler[*core.RecordEvent]{
    Func: func(e *core.RecordEvent) error {
        files := e.Record.GetUnsavedFiles("images")
        
        for _, file := range files {
            // Process image: resize, optimize, watermark, etc.
            processedReader := processImage(file.Reader)
            file.Reader = processedReader
        }
        
        return e.Next()
    },
})
```

### Post-upload actions

```go theme={null}
app.OnRecordAfterCreateSuccess().Bind(&hook.Handler[*core.RecordEvent]{
    Func: func(e *core.RecordEvent) error {
        // Files are now saved
        filenames := e.Record.GetStringSlice("attachments")
        
        // Trigger async processing: virus scan, metadata extraction, etc.
        for _, filename := range filenames {
            go processFileAsync(e.App, e.Record, filename)
        }
        
        return e.Next()
    },
})
```

### File deletion

```go theme={null}
app.OnRecordAfterDeleteSuccess().Bind(&hook.Handler[*core.RecordEvent]{
    Func: func(e *core.RecordEvent) error {
        // Files are already deleted by this point
        // Use this for cleanup in external systems
        
        app.Logger().Info(
            "Record files deleted",
            "collection", e.Record.Collection().Name,
            "record", e.Record.Id,
        )
        
        return e.Next()
    },
})
```

## Direct filesystem access

### Getting filesystem instance

```go theme={null}
fsys, err := app.NewFilesystem()
if err != nil {
    return err
}
defer fsys.Close()
```

### File operations

```go theme={null}
// Upload file
path := record.BaseFilesPath() + "/" + filename
err := fsys.UploadFile(file, path)

// Download file
reader, err := fsys.GetFile(path)

// Delete file
err := fsys.Delete(path)

// Delete directory
err := fsys.DeletePrefix(record.BaseFilesPath())

// Check if exists
exists, err := fsys.Exists(path)

// Get file attributes
attrs, err := fsys.Attributes(path)
fmt.Println("Size:", attrs.Size)
fmt.Println("Modified:", attrs.ModTime)
```

### List files

```go theme={null}
// List files in directory
files, err := fsys.List(record.BaseFilesPath())

for _, file := range files {
    fmt.Println(file.Key, file.Size)
}
```

## Performance optimization

<AccordionGroup>
  <Accordion title="Use appropriate file sizes">
    Set reasonable MaxSize limits. Smaller files upload faster and consume less storage. Consider compressing large files client-side before upload.
  </Accordion>

  <Accordion title="Optimize thumbnail sizes">
    Only generate thumbnails you actually use. Each thumbnail size requires storage space and processing time.
  </Accordion>

  <Accordion title="Enable CDN for public files">
    Use a CDN in front of your PocketBase instance to cache and serve files closer to users.
  </Accordion>

  <Accordion title="Lazy load images">
    Load images on-demand rather than all at once. Use smaller thumbnails for previews and full-size images only when needed.
  </Accordion>

  <Accordion title="Use S3 for production">
    S3-compatible storage is generally more reliable and scalable than local filesystem storage for production deployments.
  </Accordion>

  <Accordion title="Implement file cleanup">
    Regularly check for and remove orphaned files or old temporary files to save storage space.
  </Accordion>
</AccordionGroup>

## Security best practices

<AccordionGroup>
  <Accordion title="Validate file types strictly">
    Don't rely solely on MIME types - they can be spoofed. Verify file contents match expected types.
  </Accordion>

  <Accordion title="Sanitize filenames">
    PocketBase automatically sanitizes filenames, but be cautious when using user-provided filenames in other contexts.
  </Accordion>

  <Accordion title="Scan for malware">
    Implement virus scanning for uploaded files, especially for user-facing applications.
  </Accordion>

  <Accordion title="Use protected files for sensitive data">
    Set `Protected: true` for files containing sensitive information. This requires authentication to access.
  </Accordion>

  <Accordion title="Implement rate limiting">
    Limit file upload frequency and size per user to prevent abuse and resource exhaustion.
  </Accordion>

  <Accordion title="Set appropriate CORS headers">
    Configure CORS properly if files need to be accessed from different domains.
  </Accordion>
</AccordionGroup>
