DKNet

DKNet.Svc.BlobStorage.Local

Local-filesystem implementation of IBlobService for .NET developers, storing blobs under a configured root folder with path-traversal protection.

✨ Why use it?

🚀 Quick Start

dotnet new console -f net10.0 -n LocalBlobDemo
cd LocalBlobDemo
dotnet add package DKNet.Svc.BlobStorage.Local
dotnet add package Microsoft.Extensions.Logging
using DKNet.Svc.BlobStorage.Abstractions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

var config = new ConfigurationBuilder()
    .AddInMemoryCollection(new Dictionary<string, string?>
    {
        ["BlobStorage:LocalFolder:RootFolder"] = Path.Combine(Path.GetTempPath(), "blob-demo")
    })
    .Build();

var services = new ServiceCollection().AddLogging().AddLocalDirectoryBlobService(config);
using var provider = services.BuildServiceProvider();
var blobService = provider.GetRequiredService<IBlobService>();

var location = await blobService.SaveAsync(
    new BlobDetails.BlobData("reports/monthly.pdf", BinaryData.FromString("...")) { Overwrite = true });
Console.WriteLine(location); // reports/monthly.pdf

Run with dotnet run. AddLocalDirectoryBlobService(IServiceCollection, IConfiguration) binds LocalDirectoryOptions from "BlobStorage:LocalFolder" and registers IBlobService → LocalBlobService as Scoped; the call is idempotent.

🏗️ Architecture

Application code registers AddLocalDirectoryBlobService, which registers LocalBlobService as scoped, deriving from Abstractions.BlobService; LocalBlobService reads LocalDirectoryOptions and resolves every call to a path under RootFolder on the local file system.

There is no SDK client to build or cache here, unlike the cloud providers — every operation resolves a path under RootFolder and touches the file system directly through System.IO.

🔄 How it works

GetFinalPath drops a leading slash from the blob name, combines it with RootFolder and resolves it with Path.GetFullPath, then compares the result against the root prefix — ordinal on Linux, case-insensitive on Windows — throwing UnauthorizedAccessException when it escapes and otherwise reading or writing the file and returning the root-relative name.

Every operation resolves the requested name to an absolute path and re-checks it against the root before touching the file system. The diagram does not show ListItemsAsync’s directory walk or SaveAsync’s overwrite/directory- creation behavior — see the feature page below.

🧩 Features