---
title: "Local Filesystem Driver"
description: "Store objects on the local filesystem with atomic writes and sidecar metadata."
---

The local filesystem driver stores objects as files on disk. Buckets are directories, objects are files, and metadata is stored in `.meta.json` sidecar files alongside each object.

## Installation

```bash
go get github.com/xraph/trove/drivers/localdriver
```

## Usage

```go
import (
    "context"
    "github.com/xraph/trove"
    "github.com/xraph/trove/drivers/localdriver"
)

ctx := context.Background()

// Create the driver
drv := localdriver.New()

// Open with a file:// DSN
err := drv.Open(ctx, "file:///var/data/trove")
if err != nil {
    log.Fatal(err)
}

// Use with Trove
t, err := trove.Open(drv)
```

## DSN Format

The local driver supports two URI schemes:

```
file:///absolute/path/to/root          # Absolute path only
local://./relative/path                # Relative path (resolved from cwd)
local:///absolute/path                 # Absolute path
```

The `file://` scheme requires an absolute path. The `local://` scheme supports both relative and absolute paths. Relative paths are resolved to absolute from the current working directory.

### Examples

```yaml
# Absolute path
storage_driver: "file:///var/data/trove"

# Relative path (resolved from cwd)
storage_driver: "local://./storages/local"

# Absolute path with local:// scheme
storage_driver: "local:///data/archive"
```

## Driver Registry

The local driver registers itself in the global driver registry under both `file` and `local` schemes. This enables DSN-based driver resolution in multi-store mode:

```go
import _ "github.com/xraph/trove/drivers/localdriver" // registers "file" and "local"
```

## Storage Layout

```
/var/data/trove/           <-- root directory
├── photos/                <-- bucket "photos"
│   ├── beach.jpg          <-- object "beach.jpg"
│   ├── .beach.jpg.meta.json    <-- metadata sidecar
│   └── sunset.png
└── documents/             <-- bucket "documents"
    ├── report.pdf
    └── .report.pdf.meta.json
```

## Features

### Atomic Writes

Put operations use write-to-temp-then-rename for crash safety. Objects are first written to a temporary file, then atomically renamed to the final path. This prevents partial writes from corrupting existing objects.

### Sidecar Metadata

Each object can have an associated `.meta.json` sidecar file that stores:
- Content type
- Custom metadata key-value pairs

### Content Type Detection

If no content type is specified via `driver.WithContentType()`, the driver detects it from the file extension using Go's `mime` package.

### Directory-Based Buckets

Buckets map directly to subdirectories under the root. Creating a bucket creates a directory; deleting a bucket removes the directory.

## API

### Constructor

```go
func New() *LocalDriver
```

Creates a new local filesystem driver.

### SetRootDir / RootDir

```go
drv := localdriver.New()
drv.SetRootDir("/tmp/test-data")
fmt.Println(drv.RootDir()) // /tmp/test-data
```

Set the root directory directly (alternative to using `Open` with a DSN).

### Unwrap

Extract the `*LocalDriver` from a Trove handle for direct access:

```go
func Unwrap(accessor interface{ Driver() driver.Driver }) *LocalDriver
```

```go
local := localdriver.Unwrap(troveInstance)
if local != nil {
    fmt.Println("Root:", local.RootDir())
}
```

## Example: File Upload Server

```go
func handleUpload(w http.ResponseWriter, r *http.Request) {
    file, header, _ := r.FormFile("file")
    defer file.Close()

    info, err := store.Put(r.Context(), "uploads", header.Filename, file,
        driver.WithContentType(header.Header.Get("Content-Type")),
    )
    if err != nil {
        http.Error(w, err.Error(), 500)
        return
    }

    fmt.Fprintf(w, "Uploaded: %s (%d bytes)", info.Key, info.Size)
}
```

## Limitations

- No server-side copy across different root directories
- No multipart upload support (entire file is written at once)
- No pre-signed URLs
- Metadata is limited to what `.meta.json` sidecar files store
- Not suitable for high-concurrency production workloads (use S3/GCS drivers instead)
