Introduction to S3 and Cloud Storage for .NET Developers
Cloud object storage compatible with the Amazon S3 (Simple Storage Service) protocol has become the de facto standard for storing unstructured data: images, videos, backups, logs, and build artifacts. In the C# (.NET) ecosystem, working with such storage is implemented via the official AWS SDK for .NET or through lightweight clients for S3-compatible solutions (MinIO, Yandex Object Storage, DigitalOcean Spaces).
In this article, we will cover the main operations: connection, bucket creation, uploading, downloading, and deleting objects. All examples are written in C# using .NET 8.
Installing the AWS SDK for .NET Library
To work with S3 from C#, you need to install the NuGet package AWS SDK for .NET — S3:
dotnet add package AWSSDK.S3dotnet add package AWSSDK.Extensions.NETCore.Setup // for DI integrationIf you are using MinIO or other S3-compatible storage (not AWS), the same package is sufficient — it supports custom endpoints.
Basic S3 Client Configuration
Let's create a client for working with S3. The example below uses MinIO running locally on port 9000:
using Amazon;using Amazon.S3;using Amazon.Runtime;
var config = new AmazonS3Config{ ServiceURL = "http://localhost:9000", // for MinIO or other S3-compatible storage ForcePathStyle = true, // required for MinIO UseHttp = true};
var credentials = new BasicAWSCredentials("minioadmin", "minioadmin");var s3Client = new AmazonS3Client(credentials, config);For AWS S3, simply specify the region and use standard credentials:
var s3Client = new AmazonS3Client(RegionEndpoint.EUWest1);Creating and Deleting a Bucket
Creating a Bucket
public async Task CreateBucketAsync(string bucketName){ var putBucketRequest = new PutBucketRequest { BucketName = bucketName, UseClientRegion = true }; var response = await s3Client.PutBucketAsync(putBucketRequest); Console.WriteLine($"Bucket {bucketName} created. HTTP status: {response.HttpStatusCode}");}Checking if a Bucket Exists
public async Task<bool> DoesBucketExistAsync(string bucketName){ return await AmazonS3Util.DoesS3BucketExistAsync(s3Client, bucketName);}Deleting a Bucket
public async Task DeleteBucketAsync(string bucketName){ await s3Client.DeleteBucketAsync(bucketName); Console.WriteLine($"Bucket {bucketName} deleted.");}Uploading Files
The most common scenario is uploading a file to a bucket. Use PutObjectAsync:
public async Task UploadFileAsync(string bucketName, string key, string filePath){ var putRequest = new PutObjectRequest { BucketName = bucketName, Key = key, FilePath = filePath, ContentType = "application/octet-stream" };
var response = await s3Client.PutObjectAsync(putRequest); Console.WriteLine($"File {key} uploaded. ETag: {response.ETag}");}Uploading from a stream — useful for files received from a user via a web form:
public async Task UploadFromStreamAsync(string bucketName, string key, Stream inputStream){ var putRequest = new PutObjectRequest { BucketName = bucketName, Key = key, InputStream = inputStream }; await s3Client.PutObjectAsync(putRequest);}Downloading Files
You can download an object to a file or to a stream:
public async Task DownloadFileAsync(string bucketName, string key, string downloadPath){