Trove
1.x
Docs/Trove/Local Filesystem Driver
Open

Reading4 min
Updated31 Jul 2026
Sourcev1/storage/drivers/local.mdx

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.

Installation01

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

Usage02

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 Format03

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

# 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 Registry04

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:

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

Storage Layout05

/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

Features06

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.

API07

Constructor

func New() *LocalDriver

Creates a new local filesystem driver.

SetRootDir / RootDir

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:

func Unwrap(accessor interface{ Driver() driver.Driver }) *LocalDriver
local := localdriver.Unwrap(troveInstance)
if local != nil {
    fmt.Println("Root:", local.RootDir())
}

Example: File Upload Server08

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)
}

Limitations09

  • 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)