Overview
vfs provides a unified interface for working with different storage backends (local OS, Azure, S3, GCS, SFTP, etc.) in a consistent way. Rather than handling each backend independently, you interact with a core set of interfaces that abstract backend details. This allows you to:
- Seamlessly read, write, and manage files across multiple storages.
- Treat remote or cloud-based paths as if they were local (e.g. using standard
io.Reader/io.Writerinterfaces). - Simplify environment-agnostic development by keeping most logic backend-agnostic.
- Plug in or mock storage functionality to facilitate testing and modular application design.
Installation
go get github.com/c2fo/vfs/v7
Quick Start
package main
import (
"fmt"
"strings"
"io"
"log"
"github.com/c2fo/vfs/v7"
"github.com/c2fo/vfs/v7/vfssimple"
)
func main() {
// Create local OS file from a URI
osFile, err := vfssimple.NewFile("file:///tmp/example.txt")
if err != nil {
log.Fatal(err)
}
defer osFile.Close()
// Write to the file
_, err = io.Copy(osFile, strings.NewReader("Hello from vfs!"))
if err != nil {
log.Fatal(err)
}
if err := osFile.Close(); err != nil {
log.Fatal(err)
}
fmt.Println("File created and written:", osFile.URI())
}
This snippet shows the basic setup: an osFile is created from a URI and written to using standard libraries.
Listing
list.Entries streams the contents of any location as vfs.Entry values, in byte order, fetching from the backend only as you iterate:
for e, err := range list.Entries(ctx, loc, list.Glob("/.csv"), list.FilesOnly()) {
if err != nil {
return err
}
fmt.Println(e.RelPath(), e.Size())
}
- Options such as
list.Prefix,list.Recursive,list.MaxDepth,list.FilesOnly,list.DirsOnly,list.Glob,list.Match,list.Filterandlist.DirFilterchoose what is listed.list.Limitwithlist.Afterpages through a listing, and each option may be given once. Alist.Globimplies the depth it names, so it is not combined withlist.Recursive. - An entry carries its relative path, and the size and modification time the backend listed with it, so reading them costs no extra request.
Info()andInfoContext(ctx)fetch them when a backend did not list them. - An error for one unreadable subdirectory is yielded with that directory's entry and the listing continues, so a recursive listing can report which folders were and were not accessible.
list.Collectandlist.TopN(withlist.BySize,list.ByModTimeandlist.ByName) gather a listing when you do want it in memory.- A backend without native listing still works, listing files only. See list for the full API.
Location.List, Location.ListByPrefix and Location.ListByRegex are deprecated and will be removed in a future major version. They return every name in memory, in no defined order, for files only. To migrate:
| Before | After |
|---|---|
| loc.List() | list.Collect(list.Entries(ctx, loc, list.FilesOnly())), or range over list.Entries to stream |
| loc.ListByPrefix("rep") | list.Entries(ctx, loc, list.Prefix("rep"), list.FilesOnly()) |
| loc.ListByRegex(re) | list.Entries(ctx, loc, list.Match(re), list.FilesOnly()) |
Each entry's RelPath() is the name List returned, and list.Entries also reports directories unless list.FilesOnly() is given.
Advanced Usage
- See the GoDoc for deeper technical details, backend-specific options, and additional APIs.
- Specialized operations like copying between S3 locations or advanced configs (e.g. Azure credentials) are documented in each backend doc.
docs/ are generated from the Go doc comments with make docs:
* os backend
* gs backend
* s3 backend
* in-memory backend
* sftp backend
* ftp backend
* azure backend
* conformance tests
* delete options
* new file options
* authority
Community Contributed Backends
These backends are maintained in contrib/backend/ as separate Go modules:
- Dropbox backend - Dropbox cloud storage
- OneDrive backend - Microsoft OneDrive/SharePoint via Graph API
- Google Drive backend - Google Drive (My Drive and shared drives) via the Drive v3 API
- SQL FS backend - Files stored in PostgreSQL or SQLite through
database/sql(registered explicitly, not on import)
Additional Tools
- lockfile: Provides distributed locking mechanisms using vfs backends.
- vfsevents: Production-ready file event monitoring with support for real-time notifications, retry logic, and multiple watcher types (VFS polling, local fsnotify, S3 events, GCS events).
- testcontainers: Runs the shared backend conformance suites against real servers in disposable Docker containers.
FAQ
Q: Why am I seeing an empty file when using io.Copy on some backends if my source is empty?
A: An empty Reader often means the backend doesn't write a file until data is actually written. Use utils.TouchCopy if you need to ensure a zero-byte file is created.
Q: Will vfs v6 still be supported? A: Yes and no. We will continue to provide security patches and bug fixes for v6, but new features and enhancements will be added to v7.
Q: How long will v6 be supported? A: We will support v6 until the next major release, v8, is released. Then v7 will be supported until v9 is released.
Upgrading
Upgrading from v6 to v7
Please review these changes and update your code accordingly to ensure compatibility with v7.
S3 Backend
The project now uses the aws-sdk-go-v2 library instead of the deprecated, EOL aws-sdk-go. This update necessitated
these changes to the S3 backend:
- The S3 backend's
s3fs.Client()function now returns ans3.Clientwhich is a subset of AWS's sdk v2 functionality.
- The
Option.Retryfield is now anaws.Retryerinstead of arequest.Retry. Ensure that your Option logic is
Azure Backend
- Scheme for Azure has been updated from
httpstoaz. Update your code to use the new scheme. - Authority for Azure has been updated from
blob.core.windows.nettomy-container-name, such that the full URI
az://my-blob-name/path/to/file.txt rather than
https://my-storage-account-name.core.windows.net/my-container-name/path/to/file.txt.
GS Backend
- The
Options.Retryfield, with the now deprecatedvfs.Retrytype, has been moved togs.FileSystemas the newgs.Retryer
gs.NewFileSystem function using functional option gs.WithRetryer. All previous vfs.Retry
usage has been replaced with gs.WithRetryer. Update your code to use the new gs.WithRetryer functional option.
All Backends
Some methods in the Location and FileSystem interfaces have been deprecated because they use terminology that doesn't apply to all backends. They will be removed in a future release. Update your code to use the new methods. See #235.location.Volume()method which returns the authority as a string has been deprecated in favor of thelocation.Authority()method which
authority.Authority struct. Update your code to use the Authority().String() method instead of Volume().
location.ChangeDir()method has been deprecated in favor of the existinglocation.NewLocation()method. Update your code to use the
NewLocation() method instead of ChangeDir().
vfs.Optionsstruct has been deprecated in favor of using backend-specific structs.filesystem.Retry()method has been deprecated in favor of using backend-specific functional options, such asgs.WithRetryer.
NewFileSystemOption, to allow for more flexible configuration
of backends. This interface allows for more complex configuration options to be passed via the NewFileSystem function.
This will replace backend-specific chainable functions that require casting the filesystem to the backend type first. See #238.
Upgrading from v5 to v6
With v6, sftp.Options struct changed to accept an array of Key Exchange algorithms rather than a string. To update,
change the syntax of the auth commands.
"keyExchanges":"diffie-hellman-group-a256"
becomes
"keyExchanges":["diffie-hellman-group-a256"]
Contributing
- Fork it (
git clone https://github.com/c2fo/vfs.git) - Create your feature branch (
git checkout -b feature/fooBar) - Commit your changes (
git commit -am "Add some fooBar") - Push to the branch (
git push origin feature/fooBar) - Create a new Pull Request