Skip to content
Gustavo Viana edited this page Apr 23, 2026 · 15 revisions

API reference

All types live in the FileHub namespace. Each driver ships a marker interface (e.g. ILocalFileHub) that inherits from IFileHub.

IFileHub

public interface IFileHub
{
    FileDirectory Root { get; }
}

Entry point. Root is the sandbox; everything else is reached from it.

Marker Assembly
ILocalFileHub FileHub
IMemoryFileHub FileHub
IOracleObjectStorageFileHub FileHub.OracleObjectStorage
IFtpFileHub FileHub.Ftp

Use a marker when a consumer must bind to a specific backend; use IFileHub when it's backend-agnostic.

FileDirectory

public abstract class FileDirectory : FileSystemEntry
{
    public abstract FileDirectory Parent { get; }
}

Files

Sync Async Notes
CreateFile(name) CreateFileAsync(name, ct) Create or truncate.
CreateFile(name, overwrite) CreateFileAsync(name, overwrite, ct) overwrite: true ⇒ DeleteIfExists first.
OpenFile(name) OpenFileAsync(name, ct) Throws if missing.
OpenFile(name, createIfNotExists) OpenFileAsync(name, createIfNotExists, ct) Open-or-create.
TryOpenFile(name, out FileEntry) — Non-throwing.
GetFiles(pattern, offset, limit) GetFilesAsync(pattern, offset, limit, ct) See Usage → Pagination.

Directories

Sync Async Notes
CreateDirectory(name) CreateDirectoryAsync(name, ct) Accepts nested paths ("a/b/c").
OpenDirectory(name) OpenDirectoryAsync(name, ct) Throws if missing.
OpenDirectory(name, createIfNotExists) OpenDirectoryAsync(name, createIfNotExists, ct) Open-or-create.
TryOpenDirectory(name, out FileDirectory) — Non-throwing. Accepts nested paths.
GetDirectories(pattern) GetDirectoriesAsync(pattern, ct)

Common

Sync Async
ItemExists(name) ItemExistsAsync(name, ct)
Delete() / Delete(name) / DeleteIfExists(name) DeleteAsync siblings
Rename(newName) RenameAsync(newName, ct)
MoveTo(dir, name) / CopyTo(dir, name) MoveToAsync / CopyToAsync

Tiny example

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

foreach (var f in hub.Root.GetFiles("*.log"))
    Console.WriteLine(f.Path);

Nested-path behaviour is controlled by DirectoryPathMode on the hub (Local/Memory default to OpenIntermediates; OCI defaults to Direct).

FileEntry

public abstract class FileEntry : FileSystemEntry
{
    public          string Extension { get; }
    public abstract long   Length    { get; }
    public abstract FileDirectory Parent { get; }
}

Content

Sync Async
ReadAllText() / ReadAllText(Encoding) ReadAllTextAsync(ct) / ReadAllTextAsync(Encoding, ct)
ReadAllBytes() ReadAllBytesAsync(ct)
SetText(content, Encoding?) SetTextAsync(content, Encoding?, ct)
SetBytes(buffer) SetBytesAsync(buffer, ct)
GetReadStream() GetReadStreamAsync(ct)
GetWriteStream() GetWriteStreamAsync(ct)
CopyToStream(Stream) CopyToStreamAsync(Stream, ct)

Lifecycle

Sync Async
Delete() DeleteAsync(ct)
Rename(newName) RenameAsync(newName, ct)
MoveTo(dir, name) MoveToAsync(dir, name, ct)
CopyTo(newName) / CopyTo(dir, name) CopyToAsync(...) — cross-driver safe (falls back to stream copy)

Tiny example

var file = hub.Root.OpenFile("report.pdf");
await file.CopyToAsync(otherHub.Root, "report.pdf", ct);  // works across any two drivers

FileSystemEntry

Shared base for FileEntry and FileDirectory.

public abstract class FileSystemEntry : IDisposable
{
    public abstract string   Path             { get; }
    public          string   Name             { get; protected set; }
    public          bool     IsReadOnly       { get; protected set; }
    public abstract DateTime CreationTimeUtc  { get; }
    public abstract DateTime LastWriteTimeUtc { get; }

    public abstract bool Exists();
    public virtual  Task<bool> ExistsAsync(CancellationToken ct = default);

    protected void        ThrowIfReadOnly();      // used by driver writes
    protected static void ValidateName(string);   // blocks empty, '.', '..', invalid chars
}
  • CreationTimeUtc / LastWriteTimeUtc are cached values. Local/Memory read them straight from the in-process / OS state on access; cloud drivers (OCI, FTP) return whatever was last fetched and require Refresh() for a round-trip. Writes through cloud drivers update the cached length locally so file.Length after SetText is correct without a refresh.
  • Exists() may return false after Delete(); the object itself stays alive.
  • IsReadOnly = true causes every write to throw FileHubException — see Security → Read-only.

IUrlAccessible

Optional interface a FileEntry may implement when the file is reachable through a URL.

public interface IUrlAccessible
{
    bool IsPublic { get; }

    Uri        GetPublicUrl();
    Uri        GetSignedUrl(TimeSpan expiresIn);
    Task<Uri>  GetSignedUrlAsync(TimeSpan expiresIn, CancellationToken ct = default);
}
Driver Implements?
LocalFile, MemoryFile No
OracleObjectStorageFile Yes

Tiny example

if (file is IUrlAccessible url)
{
    var link = url.IsPublic
        ? url.GetPublicUrl()
        : await url.GetSignedUrlAsync(TimeSpan.FromMinutes(15), ct);
    return Redirect(link.ToString());
}
return PhysicalFile(file.Path, "application/octet-stream");

GetPublicUrl() throws InvalidOperationException if IsPublic == false. GetSignedUrl throws ArgumentOutOfRangeException when expiresIn <= TimeSpan.Zero.

IRefreshable

Optional interface implemented by FileEntry / FileDirectory whose metadata is cached and must be re-fetched explicitly. Exists so property getters can stay cheap and non-blocking — hidden async-over-sync inside a getter risks deadlock under UI / ASP.NET (classic) SynchronizationContexts.

public interface IRefreshable
{
    void Refresh();
    Task RefreshAsync(CancellationToken ct = default);
}
Driver File / Directory implement it?
LocalFile, LocalDirectory No (OS tracks it — no cache)
MemoryFile, MemoryDirectory No (state is authoritative)
OracleObjectStorageFile, OracleObjectStorageDirectory Yes
FtpFile, FtpDirectory Yes

Tiny example

var file = hub.Root.OpenFile("data.csv");   // stat'd at open, metadata populated

// ... time passes, bucket mutated externally ...

if (file is IRefreshable r) await r.RefreshAsync(ct);
Console.WriteLine(file.Length);  // server-authoritative now

Writes through the driver update the cached length as bytes stream, so the common SetText → file.Length flow works without an explicit refresh. See Usage → Refreshing metadata.

FileHubException

public class FileHubException : IOException
{
    public FileHubException(string message);
    public FileHubException(string message, Exception innerException);
}

Extends IOException so callers that already catch I/O errors match it automatically.

Thrown when… Exception type
Sandbox escape (.., absolute path, symlink out of root) FileHubException
Write on a read-only entry FileHubException
Driver wraps an I/O-shaped backend failure FileHubException
File missing and not creating FileNotFoundException
Directory missing DirectoryNotFoundException
null / empty / invalid name (.., ., path chars) ArgumentException
Stream re-open on OCI while another is live InvalidOperationException

Tiny example

try
{
    hub.Root.OpenDirectory("../escape");
}
catch (FileHubException ex)
{
    logger.LogWarning(ex, "Blocked traversal attempt");
}

Driver authors writing their own backend: wrap SDK-level failures that look like an I/O problem in FileHubException — see Custom Drivers.

Clone this wiki locally