S3Storage.Client 1.0.6

dotnet add package S3Storage.Client --version 1.0.6
                    
NuGet\Install-Package S3Storage.Client -Version 1.0.6
                    
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="S3Storage.Client" Version="1.0.6" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="S3Storage.Client" Version="1.0.6" />
                    
Directory.Packages.props
<PackageReference Include="S3Storage.Client" />
                    
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 S3Storage.Client --version 1.0.6
                    
#r "nuget: S3Storage.Client, 1.0.6"
                    
#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 S3Storage.Client@1.0.6
                    
#: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=S3Storage.Client&version=1.0.6
                    
Install as a Cake Addin
#tool nuget:?package=S3Storage.Client&version=1.0.6
                    
Install as a Cake Tool

S3Storage.Client

S3Storage.Client is a lightweight, dependency-free .NET client for AWS S3 and S3-compatible object storage providers. It signs every request with AWS Signature V4 and works with any provider that implements the S3 API.

Supported providers

Provider Factory method
AWS S3 S3ClientFactory.ForAwsS3
MinIO S3ClientFactory.ForMinIO
RustFS S3ClientFactory.ForRustFS
Cloudflare R2 S3ClientFactory.ForCloudflareR2
DigitalOcean Spaces S3ClientFactory.ForDigitalOceanSpaces
Backblaze B2 S3ClientFactory.ForBackblazeB2
Wasabi S3ClientFactory.ForWasabi
Yandex Object Storage S3ClientFactory.ForYandex
Linode (Akamai) Object Storage S3ClientFactory.ForLinode
Alibaba Cloud OSS S3ClientFactory.ForAlibabaOSS
Oracle Cloud Object Storage S3ClientFactory.ForOracleObjectStorage
Google Cloud Storage (HMAC) S3ClientFactory.ForGoogleCloudStorage
MinIO Operator (Kubernetes) S3ClientFactory.ForMinIOOperator
LocalStack S3ClientFactory.ForLocalStack
Custom endpoint S3ClientFactory.ForCustom

Install

dotnet add package S3Storage.Client

Quick start

using S3Storage;

using var client = S3ClientFactory.ForMinIO(
    host: "localhost",
    accessKey: "minioadmin",
    secretKey: "minioadmin",
    port: 9000,
    useSSL: false);

await client.CreateBucketAsync("demo-bucket");
await client.UploadTextAsync("demo-bucket", "hello.txt", "Hello from S3Storage.Client!");
var text = await client.DownloadTextAsync("demo-bucket", "hello.txt");
Console.WriteLine(text);

ASP.NET Core dependency injection

Register the client as a singleton so the internal HttpClient connection pool is reused:

builder.Services.AddSingleton<IS3Client>(_ =>
    S3ClientFactory.ForMinIO(
        host:      builder.Configuration["S3:Host"]!,
        accessKey: builder.Configuration["S3:AccessKey"]!,
        secretKey: builder.Configuration["S3:SecretKey"]!,
        port:      int.Parse(builder.Configuration["S3:Port"] ?? "9000"),
        useSSL:    bool.Parse(builder.Configuration["S3:UseSSL"] ?? "false"),
        region:    builder.Configuration["S3:Region"] ?? "us-east-1"));

appsettings.json:

{
  "S3": {
    "Host":      "localhost",
    "Port":      "9000",
    "AccessKey": "minioadmin",
    "SecretKey": "minioadmin",
    "UseSSL":    "false",
    "Region":    "us-east-1"
  }
}

Common operations

Bucket management

await client.CreateBucketAsync("my-bucket");
bool exists = await client.BucketExistsAsync("my-bucket");
List<string> buckets = await client.ListBucketsAsync();
await client.DeleteBucketAsync("my-bucket");   // bucket must be empty

Upload

// From a local file (content-type inferred from extension)
await client.UploadFileAsync("my-bucket", "reports/q3.pdf", @"C:\data\q3.pdf");

// From a stream
await using var stream = File.OpenRead(@"C:\data\photo.jpg");
await client.UploadAsync("my-bucket", "photos/photo.jpg", stream, "image/jpeg");

// Plain text
await client.UploadTextAsync("my-bucket", "notes/readme.txt", "Hello!");

// Raw bytes
byte[] data = ...;
await client.UploadBytesAsync("my-bucket", "binary/data.bin", data);

Download

byte[]  bytes = await client.DownloadBytesAsync("my-bucket", "binary/data.bin");
string  text  = await client.DownloadTextAsync("my-bucket", "notes/readme.txt");
await client.DownloadFileAsync("my-bucket", "reports/q3.pdf", @"C:\downloads\q3.pdf");

Object metadata and listing

bool exists = await client.ObjectExistsAsync("my-bucket", "notes/readme.txt");

ObjectInfo info = await client.GetObjectInfoAsync("my-bucket", "notes/readme.txt");
Console.WriteLine($"{info.Key}  {info.Size} bytes  {info.ContentType}");

// List up to 1 000 keys; optionally filter by prefix
List<ObjectInfo> objects = await client.ListObjectsAsync("my-bucket", prefix: "reports/");

Delete

await client.DeleteObjectAsync("my-bucket", "notes/readme.txt");

// Batch delete — up to 1 000 keys per call; empty list is a no-op
await client.DeleteObjectsAsync("my-bucket", new[] { "a.txt", "b.txt", "c.txt" });

Presigned URL

// Generate a GET URL valid for one hour (default) — no credentials required to use it
string url = client.GetPresignedUrl("my-bucket", "reports/q3.pdf", expirySeconds: 3600);

Multipart upload (large files)

For files larger than a few hundred MB, use the high-level MultipartUploadAsync helper. It reads the stream in parallel parts, retries nothing on failure but aborts cleanly:

await using var stream = File.OpenRead(@"C:\data\archive.zip");
await client.MultipartUploadAsync(
    bucket:      "my-bucket",
    key:         "archive.zip",
    data:        stream,
    contentType: "application/zip",
    partSize:    8 * 1024 * 1024,   // 8 MB per part (minimum 5 MB)
    parallelism: 4);                 // concurrent part uploads (max 16)

For manual control (e.g. resumable uploads), use the low-level API:

string uploadId = await client.CreateMultipartUploadAsync("my-bucket", "large.bin");
var eTags = new Dictionary<int, string>();

try
{
    eTags[1] = await client.UploadPartAsync("my-bucket", "large.bin", uploadId, 1, part1Stream);
    eTags[2] = await client.UploadPartAsync("my-bucket", "large.bin", uploadId, 2, part2Stream);
    await client.CompleteMultipartUploadAsync("my-bucket", "large.bin", uploadId, eTags);
}
catch
{
    await client.AbortMultipartUploadAsync("my-bucket", "large.bin", uploadId);
    throw;
}

Error handling

All S3 errors are surfaced as S3Exception. The message includes the HTTP status code and the provider's error code (e.g. [404] NoSuchKey: The specified key does not exist.).

try
{
    var text = await client.DownloadTextAsync("my-bucket", "missing.txt");
}
catch (S3Exception ex)
{
    Console.WriteLine(ex.Message);
}

Security notes

  • Every request is signed with AWS Signature Version 4.
  • Always use HTTPS (useSSL: true) in production environments.
  • Register the client as a DI singleton — the internal SocketsHttpHandler maintains a connection pool that is reused across calls, reducing latency and avoiding port exhaustion.
  • Never hard-code credentials; read them from environment variables, IConfiguration, or a secrets manager.

License

MIT License. See LICENSE.

Copyright (c) 2026 Aktam Abdunazarov, AbdunazarovLabs

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 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 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.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.0

    • No dependencies.

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.6 119 3/31/2026