StorageFlow.Core
1.0.0
dotnet add package StorageFlow.Core --version 1.0.0
NuGet\Install-Package StorageFlow.Core -Version 1.0.0
<PackageReference Include="StorageFlow.Core" Version="1.0.0" />
<PackageVersion Include="StorageFlow.Core" Version="1.0.0" />
<PackageReference Include="StorageFlow.Core" />
paket add StorageFlow.Core --version 1.0.0
#r "nuget: StorageFlow.Core, 1.0.0"
#:package StorageFlow.Core@1.0.0
#addin nuget:?package=StorageFlow.Core&version=1.0.0
#tool nuget:?package=StorageFlow.Core&version=1.0.0
StorageFlow.Core
StorageFlow is a provider-agnostic object storage framework for .NET. It gives application code one fluent API for AWS S3, MinIO, and RustFS while keeping provider SDKs outside business logic.
Use StorageFlow when you want typed configuration, reusable policies, streaming I/O, consistent results, and the freedom to select a registered provider per operation.
Install
Install Core and at least one provider package:
dotnet add package StorageFlow.Core
dotnet add package StorageFlow.Provider.Minio
Available providers:
Quick start
Define strongly typed policy keys:
using StorageFlow.Abstractions.Interfaces;
public sealed class DocumentsPolicy : IValidationPolicyKey;
public sealed class MediaNaming : INamingPolicyKey;
public sealed class DownloadUrl : IPresignedUrlPolicyKey;
public sealed class PublicAssets : IDeliveryUrlPolicyKey;
Register policies and a provider:
using StorageFlow.Core.DependencyInjection;
using StorageFlow.Provider.Minio;
builder.Services.AddStorageFlow(options =>
{
options.Validation.AddPolicy<DocumentsPolicy>(policy =>
{
policy.MaxFileSizeBytes = 10 * 1024 * 1024;
policy.AllowedExtensions = [".pdf", ".zip"];
policy.AllowedMimeTypes = ["application/pdf", "application/zip"];
policy.RequireValidSignature = true;
});
options.Naming
.AddPolicy<MediaNaming>(policy =>
policy.UsePattern("{yyyy}/{MM}/{slug}-{guid}{ext}"))
.AsDefault();
options.PresignedUrls.AddPolicy<DownloadUrl>(policy =>
{
policy.Expiration = TimeSpan.FromMinutes(15);
policy.HttpMethod = HttpMethod.Get;
});
options.DeliveryUrls.AddPolicy<PublicAssets>(policy => policy
.UseCdn("https://cdn.example.com")
.WithPathPrefix("assets"));
options.Providers.UseMinio(minio => minio.Configure(config =>
{
config.Endpoint = builder.Configuration["Storage:Minio:Endpoint"]!;
config.AccessKey = builder.Configuration["Storage:Minio:AccessKey"]!;
config.SecretKey = builder.Configuration["Storage:Minio:SecretKey"]!;
config.UseSSL = true;
})).AsDefault();
});
Upload a stream:
var result = await storage
.Validation<DocumentsPolicy>()
.CacheControl("private, max-age=0")
.ContentDisposition("attachment")
.Metadata("source", "documents-api")
.FromStream(stream, fileName, contentType, contentLength)
.UploadAsync("documents", cancellationToken);
if (!result.IsSuccess)
{
logger.LogWarning("{Code}: {Message}",
result.Error!.Code,
result.Error.Message);
return;
}
var objectKey = result.Value!.ObjectKey;
var etag = result.Value.ETag;
The upload stream remains owned by the caller. StorageFlow does not dispose it.
Validation
Validation policies can enforce:
- minimum and maximum file size;
- allowed and blocked extensions;
- allowed MIME types;
- magic-number/file-signature checks.
Extension and MIME allowlists are configuration driven. You can list any extension or MIME type required by your application.
Built-in magic-number validation currently recognizes:
| Format | Supported extensions | Signature |
|---|---|---|
| JPEG | .jpg, .jpeg |
JPEG SOI prefix |
| PNG | .png |
PNG signature |
.pdf |
%PDF prefix |
|
| ZIP | .zip |
Standard, empty, and spanning ZIP prefixes |
| MP3 | .mp3 |
MPEG frame prefixes or ID3 tag |
| MP4 | .mp4 |
ISO base media ftyp box |
RequireValidSignature = true validates known formats. For an extension not in
the table, StorageFlow currently skips the signature step; use a custom
IFileValidator when that format must be verified. Extension and MIME checks
still run normally.
Custom validators are resolved from dependency injection and ordered by their
Order value:
builder.Services.AddStorageFlowValidator<MalwareScanValidator>();
Naming
Naming policies support GUID, SEO, pattern, and custom strategies:
options.Naming.AddPolicy<GuidNaming>(policy => policy.UseGuid());
options.Naming.AddPolicy<SeoNaming>(policy => policy.UseSeo());
options.Naming.AddPolicy<MediaNaming>(policy =>
policy.UsePattern("{yyyy}/{MM}/{dd}/{slug}-{guid}{ext}"));
Pattern tokens: {yyyy}, {MM}, {dd}, {guid}, {slug}, {ext}, and
{timestamp}.
An operation uses its explicit naming policy, then the global default, then a GUID fallback. Provider-level definitions override a global policy with the same key.
Object operations
Download content as a stream:
var result = await storage
.Object("documents", objectKey)
.DownloadAsync(cancellationToken);
if (result.IsSuccess)
{
var download = result.Value!;
await using var content = download.Content;
await content.CopyToAsync(destination, cancellationToken);
}
DownloadResult includes content type, content length, normalized ETag, last
modified time, and read-only user metadata. The caller owns the returned
stream.
var exists = await storage
.Object("documents", objectKey)
.ExistsAsync(cancellationToken);
var deleted = await storage
.Object("documents", objectKey)
.DeleteAsync(cancellationToken);
Private and public URLs
Use a presigned URL for temporary private access:
var signed = await storage
.Object("documents", objectKey)
.GetPresignedUrlAsync<DownloadUrl>(cancellationToken);
Use a delivery URL for stable public CDN addresses. Delivery URL generation is synchronous and performs no storage or network request:
var publicUrl = storage
.Object("media", objectKey)
.GetDeliveryUrl<PublicAssets>();
Batch delivery preserves input ordering and duplicate keys:
var urls = storage
.Objects("media", objectKeys)
.GetDeliveryUrls<PublicAssets>();
Multiple providers
Register several official providers and mark one default. Select another with
the autocomplete-friendly SFProvider catalog:
var result = await storage
.Provider(SFProvider.S3)
.Validation<DocumentsPolicy>()
.FromStream(stream, fileName, contentType, contentLength)
.UploadAsync("documents", cancellationToken);
Provider selection should come from application logic or a typed profile, not from an arbitrary client-supplied string.
Results and errors
Public operations return StorageResult or StorageResult<T>. Provider SDK
exceptions do not escape the API. Common error codes include
ObjectNotFound, BucketNotFound, PermissionDenied, ProviderError,
ValidationFailed, and Unknown.
ETags are normalized without surrounding quotes, but an ETag is not guaranteed to be a content hash.
More information
StorageFlow targets .NET 9 and later and is licensed under MIT.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net9.0
- Microsoft.Extensions.DependencyInjection (>= 10.0.9)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.9)
- StorageFlow.Abstractions (>= 1.0.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 143 | 6/24/2026 |
| 1.0.0-rc.2 | 80 | 6/23/2026 |
| 1.0.0-rc.1 | 77 | 6/23/2026 |