S3Lite 1.2.0
dotnet add package S3Lite --version 1.2.0
NuGet\Install-Package S3Lite -Version 1.2.0
<PackageReference Include="S3Lite" Version="1.2.0" />
<PackageVersion Include="S3Lite" Version="1.2.0" />
<PackageReference Include="S3Lite" />
paket add S3Lite --version 1.2.0
#r "nuget: S3Lite, 1.2.0"
#:package S3Lite@1.2.0
#addin nuget:?package=S3Lite&version=1.2.0
#tool nuget:?package=S3Lite&version=1.2.0
S3Lite
Lightweight Amazon S3 and S3-compatible storage client for .NET.
S3Lite keeps the surface area small while still covering the core bucket and object operations most applications actually need. It targets AWS S3, Less3, MinIO, LocalStack, and other S3-compatible endpoints without dragging in the official AWS SDK.
Why S3Lite
- Small dependency footprint
- Simple fluent client configuration
- AWS S3 and S3-compatible endpoint support
- Anonymous access support for public buckets
- Caller-supplied
HttpClientsupport for DI, proxying, custom handlers, and connection reuse - Reverse-proxy / gateway routing that keeps the SigV4 signature bound to the upstream endpoint (useful for non-standard corporate reverse proxy configurations)
- Multi-targeted package:
netstandard2.0,netstandard2.1,net8.0, andnet10.0
New in v1.1.0
- Upgraded to
RestWrapperv3.2.0 - Added support for caller-supplied
HttpClientinstances - Reworked the automated test infrastructure around Touchstone
- Added xUnit and NUnit runner projects alongside the console runner
Installation
dotnet add package S3Lite
Quick Start
AWS S3 with Credentials
using System;
using System.Text;
using S3Lite;
using S3Lite.ApiObjects;
S3Client s3 = new S3Client()
.WithRegion("us-west-1")
.WithAccessKey("AKIAIOSFODNN7EXAMPLE")
.WithSecretKey("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY")
.WithRequestStyle(RequestStyleEnum.VirtualHostedStyle)
.WithLogger(Console.WriteLine);
ListAllMyBucketsResult buckets = await s3.Service.ListBucketsAsync();
await s3.Object.WriteAsync(
"my-bucket",
"hello.txt",
Encoding.UTF8.GetBytes("hello from s3lite"),
"text/plain");
byte[] data = await s3.Object.GetAsync("my-bucket", "hello.txt");
Console.WriteLine(Encoding.UTF8.GetString(data));
Anonymous Access for Public Buckets
If a bucket is public, simply omit credentials:
using System;
using S3Lite;
using S3Lite.ApiObjects;
S3Client s3 = new S3Client()
.WithRegion("us-west-1")
.WithRequestStyle(RequestStyleEnum.VirtualHostedStyle);
ListBucketResult result = await s3.Bucket.ListAsync("public-dataset-bucket");
Console.WriteLine(result.Contents.Count);
S3-Compatible Storage
using S3Lite;
S3Client s3 = new S3Client()
.WithHostname("localhost")
.WithPort(9000)
.WithProtocol(ProtocolEnum.Http)
.WithRegion("us-west-1")
.WithRequestStyle(RequestStyleEnum.PathStyle)
.WithAccessKey("minioadmin")
.WithSecretKey("minioadmin");
Bring Your Own HttpClient
S3Lite now exposes the caller-supplied HttpClient support added in RestWrapper v3.2.0. Use this when you already manage HttpClient instances through dependency injection, need a custom handler pipeline, or want to centralize transport settings.
using System;
using System.Net.Http;
using S3Lite;
HttpClient httpClient = new HttpClient();
httpClient.Timeout = TimeSpan.FromSeconds(30);
S3Client s3 = new S3Client(httpClient)
.WithRegion("us-east-1")
.WithHostname("s3.us-east-1.amazonaws.com")
.WithRequestStyle(RequestStyleEnum.PathStyle)
.WithAccessKey("AKIAIOSFODNN7EXAMPLE")
.WithSecretKey("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
bool exists = await s3.Bucket.ExistsAsync("my-bucket");
You can also attach one fluently:
S3Client s3 = new S3Client()
.WithHttpClient(httpClient)
.WithRegion("us-east-1");
Notes:
- The caller owns the lifetime of the supplied
HttpClient - S3Lite does not dispose a caller-supplied
HttpClient - Transport behavior such as proxying, TLS, decompression, retries, and timeouts should be configured on your
HttpClientor its handler
Routing Through a Reverse Proxy / Gateway
Some environments require S3 traffic to egress through an intermediate host that forwards to the real endpoint. For example, s3.gateway.example.com can be configured to act as a reverse proxy to s3.us-west-1.amazonaws.com.
SigV4 binds the signature to the request Host, so simply pointing the client at the gateway makes it sign for the gateway; once the gateway rewrites Host to the upstream endpoint, the signature no longer validates.
GatewayConfig solves this. When set, S3Lite sends the request to the gateway while the SigV4 signature stays bound to the upstream endpoint configured via WithHostname. Attach it with WithGateway:
using S3Lite;
S3Client s3 = new S3Client()
.WithRegion("us-west-1")
.WithHostname("s3.us-west-1.amazonaws.com") // the real upstream endpoint the signature is bound to
.WithPort(443)
.WithProtocol(ProtocolEnum.Https)
.WithRequestStyle(RequestStyleEnum.PathStyle)
.WithAccessKey("AKIAIOSFODNN7EXAMPLE")
.WithSecretKey("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY")
.WithGateway(new GatewayConfig
{
Hostname = "s3.gateway.example.com", // gateway host only, no scheme or port
Port = 8443, // omit (or 0) to reuse the client Port
Protocol = ProtocolEnum.Https // omit (or null) to reuse the client Protocol
});
bool exists = await s3.Bucket.ExistsAsync("my-bucket");
How it works:
- The connection (scheme, host, port) is created for
https://s3.gateway.example.com, so the gateway receivesHost=s3.gateway.example.com. - The signature is computed for the upstream request authority, which is not necessarily
Hostnameverbatim. ForPathStyleit is the endpoint (for examples3.us-west-1.amazonaws.com, including a non-standard port when configured); forVirtualHostedStyleit is bucket-prefixed (for examplemy-bucket.s3.us-west-1.amazonaws.com). - The gateway is expected to rewrite
Hostback to that upstream authority before forwarding upstream, so the signature validates. The path, query, and body are unchanged.
GatewayConfig.Hostname must be the gateway host only — supply the protocol via GatewayConfig.Protocol and the port via GatewayConfig.Port separately. When Protocol is null or Port is 0, the client's own Protocol / Port values are reused. Leave Gateway unset (or Hostname empty) to send requests directly to Hostname.
This is not a forward / HTTP (CONNECT) proxy. A forward proxy preserves the upstream Host end-to-end and needs no rewrite; to use one, leave Gateway unset and supply an HttpClient configured with a WebProxy via WithHttpClient instead.
Common Operations
Service APIs
ListAllMyBucketsResult buckets = await s3.Service.ListBucketsAsync();
Bucket APIs
bool exists = await s3.Bucket.ExistsAsync("my-bucket");
await s3.Bucket.WriteAsync("my-bucket", "us-west-1");
ListBucketResult objects = await s3.Bucket.ListAsync("my-bucket");
ListBucketResult filtered = await s3.Bucket.ListAsync("my-bucket", prefix: "images/");
ListBucketResult page = await s3.Bucket.ListAsync("my-bucket", continuationToken: "token-value", maxKeys: 100);
await s3.Bucket.DeleteAsync("my-bucket");
Object APIs
await s3.Object.WriteAsync("my-bucket", "notes/hello.txt", Encoding.UTF8.GetBytes("hello"));
bool exists = await s3.Object.ExistsAsync("my-bucket", "notes/hello.txt");
ObjectMetadata metadata = await s3.Object.GetMetadataAsync("my-bucket", "notes/hello.txt");
byte[] data = await s3.Object.GetAsync("my-bucket", "notes/hello.txt");
await s3.Object.DeleteAsync("my-bucket", "notes/hello.txt");
Endpoint Guidance
The right hostname depends on the request style you choose:
| Request Style | Typical Hostname | Example URL |
|---|---|---|
VirtualHostedStyle |
amazonaws.com |
https://mybucket.s3.us-west-1.amazonaws.com/mykey |
PathStyle |
s3.us-west-1.amazonaws.com |
https://s3.us-west-1.amazonaws.com/mybucket/mykey |
For S3-compatible platforms such as Less3, MinIO, or LocalStack, point Hostname and Port at your service endpoint and usually prefer PathStyle.
Key Client Properties
| Property | Description |
|---|---|
AccessKey |
Access key, or null for anonymous mode |
SecretKey |
Secret key, or null for anonymous mode |
HasCredentials |
True when both access key and secret key are configured |
Region |
Region used in request signing and URL construction |
Hostname |
Endpoint hostname |
Port |
Endpoint port |
Protocol |
Http or Https |
RequestStyle |
VirtualHostedStyle or PathStyle |
SignatureVersion |
Signature version used by the client |
HttpClient |
Optional caller-supplied HttpClient instance |
Gateway |
Optional GatewayConfig for reverse-proxy / gateway routing while signing for the upstream Hostname |
Logger |
Optional request logger callback |
Error Behavior
S3Lite throws WebException for failed requests and includes useful context in the exception Data collection, including:
StatusCodeURLRequestBodyResponseBody- S3 error metadata such as
RequestId,VersionId,Resource, andErrorCodewhen available
Automated Testing
The repository now uses Touchstone so the same shared descriptors can run through multiple hosts:
src/Test.Shared: shared Touchstone descriptors and test configurationsrc/Test.Automated: console runner usingTouchstone.Clisrc/Test.Xunit: xUnit adapter hostsrc/Test.Nunit: NUnit adapter host
Run the Console Runner
dotnet run --framework net8.0 --project src/Test.Automated -- -b my-bucket -a ACCESS_KEY -s SECRET_KEY
Optional arguments:
--endpoint <host>--port <port>--region <region>--http--https--path-style--virtual-hosted--verbose--skip-cleanup--skip-write-tests--results <path>
Run xUnit and NUnit
dotnet test src/Test.Xunit/Test.Xunit.csproj
dotnet test src/Test.Nunit/Test.Nunit.csproj
These runners read the same configuration from environment variables:
S3LITE_TEST_ENDPOINTS3LITE_TEST_PORTS3LITE_TEST_REGIONS3LITE_TEST_ACCESS_KEYS3LITE_TEST_SECRET_KEYS3LITE_TEST_BUCKETS3LITE_TEST_PROTOCOLS3LITE_TEST_REQUEST_STYLES3LITE_TEST_VERBOSES3LITE_TEST_SKIP_CLEANUPS3LITE_TEST_SKIP_WRITE_TESTS
Example Projects
src/Test.S3: interactive AWS S3 examplesrc/Test.S3Compatible: interactive S3-compatible examplesrc/Test.Script: script-style object hierarchy walkthroughsrc/Test.LargeEnumeration: large-listing exercise
Feedback and Enhancements
Encounter an issue or have an enhancement request? Please open an issue or start a discussion in the repository.
Version History
See CHANGELOG.md for release details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- AWSSignatureGenerator (>= 1.0.12)
- PrettyId (>= 2.0.1)
- RestWrapper (>= 3.2.0)
-
.NETStandard 2.1
- AWSSignatureGenerator (>= 1.0.12)
- PrettyId (>= 2.0.1)
- RestWrapper (>= 3.2.0)
-
net10.0
- AWSSignatureGenerator (>= 1.0.12)
- PrettyId (>= 2.0.1)
- RestWrapper (>= 3.2.0)
-
net8.0
- AWSSignatureGenerator (>= 1.0.12)
- PrettyId (>= 2.0.1)
- RestWrapper (>= 3.2.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on S3Lite:
| Package | Downloads |
|---|---|
|
Blobject.AmazonS3Lite
BLOB storage client for Amazon S3 (including compatible storage e.g. Minio, Less3, Ceph, View) using a lightweight, non-AWS SDK. Refer to other Blobject packages for other storage repository types. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Reverse-proxy / gateway routing via GatewayConfig and S3Client.WithGateway.