AWS S3 implementation of IBlobService for .NET developers, also compatible with S3-compatible services such as
MinIO and Cloudflare R2.
AddS3BlobService(configuration) binds credentials, endpoint, and bucket, and
wires IBlobService without you managing an AmazonS3Client lifetime.ForcePathStyle and an explicit endpoint cover self-hosted and
compatible services.GetPublicAccessUrl returns a pre-signed URL.dotnet new console -f net10.0 -n S3Demo
cd S3Demo
dotnet add package DKNet.Svc.BlobStorage.AwsS3
dotnet add package Microsoft.Extensions.Logging
using DKNet.Svc.BlobStorage.Abstractions;
using DKNet.Svc.BlobStorage.AwsS3;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
var config = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string?>
{
["BlobService:S3:ConnectionString"] = "https://s3.ap-southeast-1.amazonaws.com",
["BlobService:S3:BucketName"] = "app-documents"
})
.Build();
var services = new ServiceCollection().AddLogging().AddS3BlobService(config);
using var provider = services.BuildServiceProvider();
var blobService = provider.GetRequiredService<IBlobService>();
var location = await blobService.SaveAsync(
new BlobDetails.BlobData("reports/monthly.pdf", BinaryData.FromString("...")) { Overwrite = true });
Console.WriteLine(location);
AddS3BlobService(IServiceCollection, IConfiguration) binds S3Options from the "BlobService:S3" section and
registers IBlobService → S3BlobService as a process-lifetime singleton; the call is idempotent (safe to call more
than once).
S3BlobService is IDisposable and caches its AmazonS3Client; the diagram cannot show that the client is built
lazily — on the first operation, not at registration time — which is covered in the next section.
The client is not built at registration — it is built lazily on the first operation, and that first operation is
also what creates the bucket. A Lazy<Task<AmazonS3Client>> makes this race-safe: concurrent first callers all await
the same build instead of each paying their own ListBuckets/PutBucket round trip. The diagram does not show the
per-call SaveAsync path (validate, conflict check, upload) — see the feature page below.
IBlobService contract, shared models, and validation that application code should depend on directly.