Lightweight, unified file system API for .NET. Read, write, copy, move, sync and version files the same way whether they live on the local disk, an in-memory virtual tree, or any other TagBites.IO-supported storage (FTP, SFTP, SMB, WebDAV, HTTP, ZIP, Dropbox, OneDrive, Google Cloud Storage, Azure Blob Storage, S3, ...).
The FileSystem API stays the same when the underlying storage changes - local disk in development, S3 or FTP in production - so the rest of the code does not change.
System.IO covers the local disk. Every other storage arrives with its own client - FluentFTP, AWSSDK.S3, Azure.Storage.Blobs, Dropbox.Api - and its own vocabulary for the same four operations. Code written against one of them does not move to another.
Microsoft.Extensions.FileProviders unifies reading. TagBites.IO unifies reading, writing, copying, moving and synchronizing, and adds one API for the parts that usually get hand-rolled per storage:
source.SyncWith(destination, FileSystemSynchronizeMode.OneWayWithRemoveNotExisting);
source.Copy(destination, overwrite: true);
file.HasEqualContent(other); // by hash where the provider exposes oneSwapping the storage is a one-line change, which makes local disk in development and remote storage in production a configuration decision rather than a rewrite. In tests, FileSystem.CreateMemory() replaces the disk entirely, with no temporary directories to clean up.
Where it does not help. It is an abstraction over the common denominator, so storage-specific features - S3 object tagging, Dropbox sharing links, SMB ACLs - are not exposed; reach for the native client there. Capability differs between providers and is reported rather than emulated (SupportsWriteOperations, SupportsIsReadOnlyMetadata, SupportsWatcher, ...), so code that needs a capability must check for it. File history exists in the API, but no shipped provider implements it yet.
dotnet add package TagBites.IO
Targets netstandard2.0 and netstandard2.1. No dependencies.
Local disk, an in-memory file system, and a virtual composed tree are built in. Remote storages ship as separate packages that plug into the same API:
| Package | Storage |
|---|---|
| TagBites.IO.Ftp | FTP/FTPS |
| TagBites.IO.Sftp | SFTP |
| TagBites.IO.Smb | SMB/CIFS network shares |
| TagBites.IO.WebDav | WebDAV |
| TagBites.IO.Http | Read-only HTTP |
| TagBites.IO.Zip | ZIP archives |
| TagBites.IO.Dropbox | Dropbox |
| TagBites.IO.OneDrive | OneDrive (Microsoft Graph) |
| TagBites.IO.Google | Google Cloud Storage |
| TagBites.IO.AzureBlob | Azure Blob Storage |
| TagBites.IO.S3 | Amazon S3 / S3-compatible storage (e.g. Cloudflare R2) |
| TagBites.IO.WinDrive | Mount any of the above as a Windows drive letter (Dokan) |
using TagBites.IO;
// Built-in: local disk
var fs = FileSystem.Local;
// Or an in-memory file system, useful for tests
// var fs = FileSystem.CreateMemory();
var directory = fs.GetDirectory("/some/folder");
directory.Create();
var file = fs.GetFile("/some/folder/file.txt");
file.WriteAllText("Hello world!");
var content = file.ReadAllText(); // "Hello world!"
foreach (var f in directory.GetFiles(recursive: true))
Console.WriteLine(f.FullName);Every provider package (TagBites.IO.Ftp, TagBites.IO.S3, ...) exposes the same shape - a static Create(...) method returning a FileSystem - so the code above works unchanged regardless of the storage:
using TagBites.IO.Ftp;
var fs = FtpFileSystem.Create("ftp.example.com", "user", "password");
var file = fs.GetFile("/reports/2024/summary.csv");using TagBites.IO;
sourceFile.Copy(destinationFile, overwrite: true);
sourceDirectory.Move(destinationDirectory);
sourceDirectory.SyncWith(destinationDirectory, FileSystemSynchronizeMode.OneWayWithRemoveNotExisting);Every read/write operation has an async counterpart (ReadAllTextAsync, WriteAllTextAsync, CopyAsync, SyncWithAsync, ...).
The VirtualDirectory and VirtualDirectoryEntry types (IO/Virtual) mount links from several file systems - local, FTP, ZIP, cloud storage - as entries of a single logical directory tree, so consumers only ever see one FileSystem.