Local Filesystem Driver
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
go get github.com/xraph/trove/drivers/localdriverUsage
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 pathThe 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 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:
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.jsonFeatures
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
func New() *LocalDriverCreates a new local filesystem driver.
SetRootDir / RootDir
drv := localdriver.New()
drv.SetRootDir("/tmp/test-data")
fmt.Println(drv.RootDir()) // /tmp/test-dataSet 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 }) *LocalDriverlocal := localdriver.Unwrap(troveInstance)
if local != nil {
fmt.Println("Root:", local.RootDir())
}Example: File Upload Server
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.jsonsidecar files store - Not suitable for high-concurrency production workloads (use S3/GCS drivers instead)