Tigris.Storage 1.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Tigris.Storage --version 1.0.0
                    
NuGet\Install-Package Tigris.Storage -Version 1.0.0
                    
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="Tigris.Storage" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Tigris.Storage" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Tigris.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 Tigris.Storage --version 1.0.0
                    
#r "nuget: Tigris.Storage, 1.0.0"
                    
#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 Tigris.Storage@1.0.0
                    
#: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=Tigris.Storage&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Tigris.Storage&version=1.0.0
                    
Install as a Cake Tool

Tigris.Storage

A reusable .NET 8 class library for uploading, deleting, and resolving URLs for images, videos, and documents using Tigris (S3-compatible object storage).


Installation

dotnet add package Tigris.Storage

Configuration

Add the following to your appsettings.json:

Single Bucket (simplest)

All three services share one bucket.

"Tigris": {
  "EndpointUrl": "https://fly.storage.tigris.dev",
  "AccessKeyId": "your-access-key-id",
  "SecretAccessKey": "your-secret-access-key",
  "BucketName": "my-bucket",
  "CustomDomainUrl": "https://cdn.example.com"
}

CustomDomainUrl is optional. When omitted, the public URL is constructed from EndpointUrl and BucketName.

Named Buckets (multiple buckets)

Each service maps to its own bucket.

"Tigris": {
  "EndpointUrl": "https://fly.storage.tigris.dev",
  "AccessKeyId": "your-access-key-id",
  "SecretAccessKey": "your-secret-access-key",
  "Buckets": {
    "images":    { "BucketName": "my-images",    "CustomDomainUrl": "https://img.cdn.com" },
    "videos":    { "BucketName": "my-videos" },
    "documents": { "BucketName": "my-docs" }
  }
}

Public vs Private Buckets

Each bucket can be marked as public or private. This controls ACL and URL behaviour:

"Buckets": {
  "images":    { "BucketName": "my-images", "IsPublic": true },
  "documents": { "BucketName": "my-docs",   "IsPublic": false, "DefaultPresignedUrlExpiry": "01:00:00" }
}
Setting Default Description
IsPublic true Public buckets return a permanent URL. Private buckets return a presigned GET URL.
DefaultPresignedUrlExpiry 1 hour How long presigned URLs are valid (applies to private buckets and explicit presigned calls).

Registration

Single Bucket

// Program.cs
builder.Services.AddTigrisStorage(builder.Configuration);

Named Buckets

// Program.cs
builder.Services.AddTigrisStorage(builder.Configuration, buckets =>
{
    buckets.UseImageBucket("images");
    buckets.UseVideoBucket("videos");
    buckets.UseDocumentBucket("documents");
});

You only need to register the services you use. If your app does not handle documents, simply omit UseDocumentBucket.


Usage

Inject the service interfaces via constructor injection.

Images

public class ProfileController(ITigrisImageService images) : ControllerBase
{
    [HttpPost("upload")]
    public async Task<IActionResult> Upload(IFormFile file)
    {
        var result = await images.UploadFormFileAsync(file, folder: "avatars");

        if (!result.IsSuccess)
            return BadRequest(new { result.Message, result.ErrorCode });

        // Store result.FileKey in your database — not FileUrl.
        // For private buckets, FileUrl is a presigned URL that expires.
        return Ok(new { result.FileKey, result.FileUrl });
    }

    [HttpPost("upload-base64")]
    public async Task<IActionResult> UploadBase64([FromBody] string base64Image)
    {
        var result = await images.UploadBase64ImageAsync(base64Image, folder: "avatars");

        if (!result.IsSuccess)
            return BadRequest(new { result.Message, result.ErrorCode });

        return Ok(new { result.FileKey, result.FileUrl });
    }

    [HttpDelete("{fileKey}")]
    public async Task<IActionResult> Delete(string fileKey)
    {
        var deleted = await images.DeleteImageAsync(fileKey);
        return deleted ? Ok() : StatusCode(500);
    }

    // Generate a fresh presigned URL from a stored FileKey
    [HttpGet("{fileKey}/url")]
    public IActionResult GetUrl(string fileKey)
    {
        var url = images.GetImageUrl(fileKey);
        return Ok(new { url });
    }
}

Videos

public class VideoController(ITigrisVideoService videos) : ControllerBase
{
    [HttpPost("upload")]
    public async Task<IActionResult> Upload(IFormFile file, CancellationToken ct)
    {
        var result = await videos.UploadVideoAsync(file, folder: "clips", cancellationToken: ct);

        if (!result.IsSuccess)
            return BadRequest(new { result.Message, result.ErrorCode });

        return Ok(new { result.FileKey, result.FileUrl });
    }

    [HttpDelete("{fileKey}")]
    public async Task<IActionResult> Delete(string fileKey)
    {
        var deleted = await videos.DeleteVideoAsync(fileKey);
        return deleted ? Ok() : StatusCode(500);
    }
}

Documents

public class DocumentController(ITigrisDocumentService documents) : ControllerBase
{
    [HttpPost("upload")]
    public async Task<IActionResult> Upload(IFormFile file, CancellationToken ct)
    {
        var result = await documents.UploadDocumentAsync(file, folder: "reports", cancellationToken: ct);

        if (!result.IsSuccess)
            return BadRequest(new { result.Message, result.ErrorCode });

        return Ok(new { result.FileKey, result.FileUrl });
    }

    [HttpDelete("{fileKey}")]
    public async Task<IActionResult> Delete(string fileKey)
    {
        var deleted = await documents.DeleteDocumentAsync(fileKey);
        return deleted ? Ok() : StatusCode(500);
    }
}

Presigned URLs

Use presigned URLs to grant temporary access to private files, or to let clients upload directly to Tigris without routing bytes through your server.

// Generate a time-limited GET URL for a private file
var downloadUrl = images.GetPresignedUrl("avatars/3f2a1b.jpg", expiry: TimeSpan.FromMinutes(15));

// Generate a presigned PUT URL for direct client-side upload
var uploadUrl = images.GetPresignedUploadUrl("avatars/3f2a1b.jpg", "image/jpeg", expiry: TimeSpan.FromMinutes(5));

API Reference

TigrisUploadResponse

All upload methods return TigrisUploadResponse.

Property Type Description
IsSuccess bool Whether the upload succeeded
Message string Error message on failure, empty on success
FileKey string? Storage key (e.g. avatars/3f2a1b.jpg). Always persist this — not FileUrl
FileUrl string? URL at time of upload. Permanent for public buckets, presigned (expiring) for private buckets
ErrorCode TigrisErrorCode Structured error code for programmatic handling
TigrisErrorCode
Value Meaning
None No error (success)
InvalidFormat File extension is not allowed
FileTooLarge File exceeds the size limit
UploadFailed Upload failed due to a storage or network error
Cancelled Upload was cancelled via CancellationToken

ITigrisImageService

Method Description
UploadFormFileAsync(file, fileName?, folder?, quality?, maxWidth?) Upload from IFormFile. Resizes and compresses automatically. Default quality: 75, max width: 1920px
UploadBase64ImageAsync(base64, fileName?, folder?, quality?, maxWidth?) Upload from a base64 data URI. Max size: 100MB
DeleteImageAsync(fileKey) Delete an image by its storage key
GetImageUrl(fileKey, expiry?) Permanent URL for public buckets; presigned GET URL for private buckets
GetPresignedUrl(fileKey, expiry?) Presigned GET URL regardless of bucket visibility
GetPresignedUploadUrl(fileKey, contentType, expiry?) Presigned PUT URL for direct client-side uploads

Supported formats: JPG, JPEG, PNG


ITigrisVideoService

Method Description
UploadVideoAsync(file, fileName?, folder?, cancellationToken?) Upload a video using S3 multipart upload (10MB parts). Max size: 500MB
DeleteVideoAsync(fileKey) Delete a video by its storage key
GetVideoUrl(fileKey, expiry?) Permanent URL for public buckets; presigned GET URL for private buckets
GetPresignedUrl(fileKey, expiry?) Presigned GET URL regardless of bucket visibility
GetPresignedUploadUrl(fileKey, contentType, expiry?) Presigned PUT URL for direct client-side uploads

Supported formats: MP4, WebM, MOV


ITigrisDocumentService

Method Description
UploadDocumentAsync(file, fileName?, folder?, cancellationToken?) Upload a document. Max size: 50MB
DeleteDocumentAsync(fileKey) Delete a document by its storage key
GetDocumentUrl(fileKey, expiry?) Permanent URL for public buckets; presigned GET URL for private buckets
GetPresignedUrl(fileKey, expiry?) Presigned GET URL regardless of bucket visibility
GetPresignedUploadUrl(fileKey, contentType, expiry?) Presigned PUT URL for direct client-side uploads

Supported formats: PDF, DOC, DOCX, PPT, PPTX, XLS, XLSX


File Keys

When you don't provide a fileName, a UUID is generated automatically (e.g. 3f2a1b.jpg).

The folder parameter prefixes the key: folder/fileName (e.g. avatars/3f2a1b.jpg).

Always store FileKey in your database — not FileUrl. For private buckets, FileUrl is a presigned URL that expires shortly after upload. Use GetImageUrl / GetVideoUrl / GetDocumentUrl with the stored key to regenerate a fresh URL on demand.


Requirements

  • .NET 8
  • ASP.NET Core (the package uses IFormFile)
  • A Tigris account with a bucket and API credentials: tigrisdata.com
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  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. 
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.2 142 2/27/2026
1.0.1 103 2/27/2026
1.0.0 102 2/27/2026