Skip to content

Driver Local

Gustavo Viana edited this page Aug 4, 2026 · 8 revisions

Driver: Local (disk)

Wraps System.IO.File / System.IO.Directory. Ships in the core FileHub package.

using FileHub.Local;

var hub = new LocalFileHub(@"C:\data");

The root is created automatically if it doesn't exist.

Constructors

Constructor Use
LocalFileHub(rootPath) Absolute or relative path. Relative resolves against the current working directory. Leading ~ resolves against AppDomain.CurrentDomain.BaseDirectory.
// Absolute
var hub = new LocalFileHub(@"C:\data");

// XDG-style — compute the full path up-front
var root = Path.Combine(
    Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
    "MyApp", "data");
var hub = new LocalFileHub(root);

Nested directory paths ("a/b/c") resolve in one shot: Directory.CreateDirectory creates every intermediate in a single syscall, and TryOpenDirectory checks the full path with one Directory.Exists.

Copy / move progress

CopyTo and MoveTo (and their async siblings) accept an optional IProgress<TransferStatus>. Transfers stream through a chunked read/write loop and report byte-level progress as they go.

var progress = new Progress<TransferStatus>(s =>
    Console.WriteLine($"{s.BytesTransferred}/{s.TotalBytes}"));
hub.Root.OpenFile("big.bin").CopyTo(dstDir, "big.bin", progress);

Overwrite

CopyTo / MoveTo default to overwrite: false: an existing destination throws FileAlreadyExistsException and the source stays intact, nothing is written. A file never overwrites a directory. Pass overwrite: true to replace an existing destination. A file CopyTo / MoveTo onto an existing destination is refused unless overwrite: true.

For a directory MoveTo(dir, name, overwrite: false) the atomic Directory.Move fast path is only taken when the destination does not exist. An existing destination throws FileAlreadyExistsException; pass overwrite: true to merge into it (copy + delete) instead of the atomic move.

Rename never overwrites: renaming onto a name already taken (file or directory) throws FileAlreadyExistsException, and any raw System.IO.IOException from File.Move / Directory.Move is wrapped so no BCL exception leaks to callers.

Interface

public interface ILocalFileHub : IFileHub { }
public class    LocalFileHub : ILocalFileHub { ... }

Inject ILocalFileHub when you want to bind to the local driver specifically; IFileHub when backend-agnostic.

Public LocalFile constructor

A LocalFile can also be built directly, anchored to an existing LocalDirectory:

var directory = (LocalDirectory)hub.Root.OpenDirectory("reports", createIfNotExists: true);
var reference = new LocalFile(directory, "q4.pdf");

if (!reference.Exists())
    reference.SetBytes(bytes);

The file isn't created on disk until you write to it (SetText, SetBytes, GetWriteStream). Raw disk paths are deliberately not accepted — the reference always carries the hub's sandbox root.

Access denied

An OS access-denied error surfaces as UnauthorizedAccessException, propagated as-is — the Local driver no longer wraps it in FileHubException. This matches every other backend.

Rename

Rename(newName) is leaf-only: a newName containing / or \ throws ArgumentException. Use MoveTo to relocate an entry into a different directory.

Sandbox

Resolved against the root and checked on every call. .., absolute paths, and separators in leaf names all throw — see Security.

Symlinks (Local-specific)

  • GetFiles / GetDirectories skip any entry with FileAttributes.ReparsePoint (symlinks, junctions, mount points) on both target frameworks.
  • On net8.0, path resolution additionally calls FileSystemInfo.ResolveLinkTarget(true) — a symlink whose final target is outside the root is rejected when opened by name.

Existence probes

FileExists(name) and DirectoryExists(name) map directly to System.IO.File.Exists / System.IO.Directory.Exists after sandbox resolution. Each is a single OS call.

Nested paths in CreateFile / OpenFile / TryOpenFile

CreateFile("a/b/c.txt") (and OpenFile / TryOpenFile) creates the file at the nested location, auto-creating the intermediate directories with Directory.CreateDirectory first. Both / and \ separators work; .. is rejected with FileHubException.

hub.Root.CreateFile("reports/2026/q1.pdf").SetText("...");
// Equivalent to: CreateDirectory("reports/2026"); then CreateFile("q1.pdf").

Typical example

var hub = new LocalFileHub(@"C:\data");

var logs = hub.Root.CreateDirectory("2026/01/logs");
logs.CreateFile("app.log").SetText("ready");

foreach (var f in hub.Root.GetFiles("*.log"))
    Console.WriteLine($"{f.Name}: {f.Length} bytes");

Every method, signature, and async counterpart: API reference.

Clone this wiki locally