Yusr.Storage 1.0.61

dotnet add package Yusr.Storage --version 1.0.61
                    
NuGet\Install-Package Yusr.Storage -Version 1.0.61
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Yusr.Storage" Version="1.0.61" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Yusr.Storage" Version="1.0.61" />
                    
Directory.Packages.props
<PackageReference Include="Yusr.Storage" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Yusr.Storage --version 1.0.61
                    
#r "nuget: Yusr.Storage, 1.0.61"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Yusr.Storage@1.0.61
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Yusr.Storage&version=1.0.61
                    
Install as a Cake Addin
#tool nuget:?package=Yusr.Storage&version=1.0.61
                    
Install as a Cake Tool

Yusr.Storage

Yusr.Storage is a robust, lightweight, and highly efficient S3-compatible file storage abstraction for .NET applications.

While it is optimized for Wasabi and MinIO (utilizing ForcePathStyle), it works seamlessly with Amazon S3 and any other S3-compatible storage provider. It provides a clean API for standard file operations and includes a powerful, state-based DTO system (StorageFile) designed to make handling file uploads/updates from modern frontend frameworks (React, Angular, Vue) incredibly easy.


What it Provides

  1. Standard S3 Operations: Upload, Delete, and retrieve Metadata using standard Streams.
  2. Pre-Signed URLs: Securely serve private files to users with expiring URLs.
  3. Smart State-Based File Handling: The StorageFile DTO tracks file state (New, Unchanged, Delete). You can pass a Base64 string from your frontend, and the library automatically figures out whether to upload a new file, delete an old one, or do nothing.
  4. Batch Processing: Easily process lists or dictionaries of files concurrently.
  5. Built-in Dependency Injection: Ready to be plugged into any modern .NET Core / .NET 5+ application.

Installation

Install the package via NuGet Package Manager Console:

Install-Package Yusr.Storage

Or via the .NET CLI:

dotnet add package Yusr.Storage

Configuration & Setup

1. appsettings.json

Add your storage credentials to your configuration file:

{
  "FilesStorage": {
    "AccessKey": "YOUR_ACCESS_KEY",
    "SecretKey": "YOUR_SECRET_KEY",
    "BucketName": "your-bucket-name",
    "ServiceURL": "https://s3.eu-central-1.wasabisys.com" // e.g., Wasabi, MinIO, or AWS URL
  }
}

2. Dependency Injection (Program.cs or Startup.cs)

Register the configuration and the service in your DI container:

using Yusr.Storage.Abstractions.Options;
using Yusr.Storage.Abstractions.Services;
using Yusr.Storage.Providers;

var builder = WebApplication.CreateBuilder(args);

// 1. Bind Options
builder.Services.Configure<FilesStorageOptions>(
    builder.Configuration.GetSection(FilesStorageOptions.SectionName));

// 2. Register the Service
builder.Services.AddScoped<IFilesStorage, WasabiService>();

Usage Examples

Inject IFilesStorage into your controllers or services to start managing files.

public class DocumentService
{
    private readonly IFilesStorage _storage;

    public DocumentService(IFilesStorage storage)
    {
        _storage = storage;
    }
}

1. Uploading a File (Stream)

Upload a standard file stream directly to your bucket.

public async Task UploadProfilePicture(Stream fileStream)
{
    string path = "users/profiles/user-123.jpg";
    string contentType = "image/jpeg";
    
    var result = await _storage.UploadFileAsync(fileStream, path, contentType);

    if (result.Success)
    {
        Console.WriteLine($"File uploaded successfully to: {result.Path}");
    }
    else
    {
        Console.WriteLine($"Upload failed: {result.Error}");
    }
}

2. Generating Pre-Signed URLs

Generate a secure, temporary URL to allow clients to view or download private files.

public string GetDownloadLink(string fileKey)
{
    // Generates a URL valid for 60 minutes (default is 1440 mins / 24 hours)
    string? signedUrl = _storage.GenerateSignedUrl(fileKey, expiresInMinutes: 60);
    
    return signedUrl ?? "File not found";
}

3. Deleting a File

Remove a file from the bucket using its key/path.

public async Task DeleteDocument(string fileKey)
{
    var result = await _storage.DeleteFileAsync(fileKey);
    
    if (result.Success)
        Console.WriteLine("File deleted!");
}

4. The "Smart" Update System (Frontend to Backend)

This library shines when handling updates from a frontend application. Instead of writing complex logic to check if a user uploaded a new file, deleted an existing one, or kept it the same, you use the StorageFile DTO.

Frontend JSON Payload Example:

{
  "status": 1, // 0 = Unchanged, 1 = New, 2 = Delete
  "base64File": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=",
  "extension": ".png",
  "contentType": "image/png"
}

Backend Processing:

public async Task<string?> UpdateUserProfileImage(StorageFile incomingFile)
{
    // This single method handles everything:
    // - If Status == New: Decodes Base64 and uploads it.
    // - If Status == Delete: Deletes the old file from S3.
    // - If Status == Unchanged: Does nothing and returns the existing path.
    
    string? finalPath = await _storage.HandleUpdatingFile(
        storageFiles: incomingFile, 
        pathPrefix: "users/avatars/"
    );

    return finalPath; // Save this path to your database
}

5. Batch Processing (Multiple Files)

If a user uploads a gallery of images, you can process them concurrently.

Processing a List:

public async Task<List<string>> UploadGallery(List<StorageFile> galleryFiles)
{
    // Uploads/Updates/Deletes all files concurrently
    List<string> savedPaths = await _storage.HandleUpdatingFiles(galleryFiles, "galleries/summer-trip/");
    
    return savedPaths;
}

Processing a Dictionary (Useful for mapping files to specific Entity IDs):

public async Task UpdateDocuments(Dictionary<long, StorageFile> documentsWithIds)
{
    // Key: Document ID, Value: StorageFile DTO
    Dictionary<long, string> results = await _storage.HandleUpdatingFilesWithIds(documentsWithIds, "docs/");
    
    foreach(var result in results)
    {
        Console.WriteLine($"Document ID {result.Key} saved to path: {result.Value}");
    }
}

6. Working with Metadata

You can attach custom metadata to files during upload and retrieve it later.

Uploading with Metadata:

var metadata = new Dictionary<string, string>
{
    { "UploadedBy", "User-789" },
    { "Department", "HR" }
};

await _storage.UploadFileAsync(stream, "hr/doc1.pdf", "application/pdf", metadata);

Retrieving Metadata:

var metadata = await _storage.GetFileMetadataAsync("hr/doc1.pdf");

if (metadata != null && metadata.ContainsKey("UploadedBy"))
{
    Console.WriteLine($"Uploader: {metadata["UploadedBy"]}");
}

7. Extracting Keys from URLs

If you have a full S3 URL and need just the database key (path), use this utility:

string fullUrl = "https://s3.wasabisys.com/my-bucket/users/profiles/pic.jpg";
string? key = _storage.ExtractKeyFromUrl(fullUrl);

// Result: "users/profiles/pic.jpg"

Architecture & Enums Reference

StorageFileStatus Enum

Used by the frontend to tell the backend what to do with the file.

  • 0 - Unchanged: The file wasn't modified.
  • 1 - New: A new file is provided in Base64File.
  • 2 - Delete: The existing file should be removed.

StorageFile Class

When sending data to the client, you can easily initialize this class to generate a signed URL automatically:

// In your GET endpoint:
var fileDto = new StorageFile("users/profiles/pic.jpg", _storage);
// fileDto.Url is now a secure, pre-signed URL!
// fileDto.Status is automatically set to Unchanged.

Contributing

Contributions, issues, and feature requests are welcome! Feel free to check the issues page.

License

This project is licensed under the MIT License.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

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.61 91 4/18/2026
1.0.61-alpha.0.1 47 4/18/2026
1.0.60 87 4/18/2026
1.0.58 93 4/18/2026
1.0.58-alpha.0.2 49 4/18/2026
1.0.58-alpha.0.1 45 4/18/2026
1.0.57 97 4/18/2026
1.0.57-alpha.0.1 55 4/18/2026
1.0.56 101 4/12/2026
1.0.56-alpha.0.1 53 4/12/2026
1.0.55 90 4/10/2026
1.0.55-alpha.0.1 61 4/10/2026
1.0.53 99 4/9/2026
1.0.53-alpha.0.1 56 4/9/2026
1.0.52 95 4/4/2026
1.0.52-alpha.0.1 52 4/4/2026
1.0.51 116 4/4/2026
1.0.51-alpha.0.1 50 4/4/2026
1.0.50 97 4/4/2026
1.0.50-alpha.0.1 52 4/4/2026
Loading failed