Skip to content
Shiny.Maui.Shell v6 support for AI routing tools Learn More

AWS S3 Uploads

Shiny HTTP Transfers includes a built-in fluent builder for uploading files to AWS S3. The AwsS3UploadRequest handles all S3-specific headers and AWS Signature V4 authentication using only HttpClient and System.Security.Cryptography — no AWS SDK required. It produces a standard HttpTransferRequest that runs as a background transfer.

IHttpTransferManager manager; // injected
var request = new AwsS3UploadRequest("/path/to/file.pdf")
.WithBucket("my-bucket", "us-east-1")
.WithCredentials(accessKeyId, secretAccessKey)
.Build();
await manager.Queue(request);

AWS S3 supports two authentication methods.

A presigned URL provides scoped, time-limited access without exposing your AWS credentials on the device. Generate it server-side, then pass it to the client.

var request = new AwsS3UploadRequest("/path/to/file.pdf")
.WithPresignedUrl("https://my-bucket.s3.us-east-1.amazonaws.com/file.pdf?X-Amz-Algorithm=AWS4-HMAC-SHA256&...")
.Build();

For server-side or trusted environments where AWS credentials are available. Supports temporary credentials via session tokens (e.g. from STS AssumeRole).

var request = new AwsS3UploadRequest("/path/to/file.pdf")
.WithBucket("my-bucket", "us-east-1")
.WithCredentials(accessKeyId, secretAccessKey, sessionToken)
.Build();

This computes the full AWS Signature V4 authorization header using UNSIGNED-PAYLOAD, so large files don’t need to be hashed upfront.

MethodDescription
WithBucket(bucket, region)Sets the URI to https://{bucket}.s3.{region}.amazonaws.com/{key}
WithObjectKey(key)Set the S3 object key (defaults to local filename)
WithCustomUri(uri)Use a custom endpoint (e.g. MinIO, LocalStack, S3-compatible services)
WithPresignedUrl(url)Authenticate with a presigned URL
WithCredentials(accessKey, secretKey, sessionToken?)Authenticate with IAM credentials (Signature V4)
WithContentType(type)Set the Content-Type header (defaults to application/octet-stream)
WithStorageClass(class)Set the storage class (e.g. STANDARD_IA, GLACIER, INTELLIGENT_TIERING)
WithMeteredConnection()Allow upload on metered/cellular networks
WithHeader(key, value)Add a custom HTTP header
PropertyTypeDescription
LocalFilePathstringPath to the file being uploaded
Identifierstring?Transfer identifier (auto-generated if not set)
BucketNamestring?S3 bucket name
Regionstring?AWS region (e.g. us-east-1)
ObjectKeystring?S3 object key (defaults to local filename)
UseMeteredConnectionboolAllow metered network transfers
HeadersDictionary<string, string>Custom headers

When using WithCredentials(), Build() automatically sets these headers:

HeaderValue
Host{bucket}.s3.{region}.amazonaws.com
x-amz-dateCurrent UTC timestamp in ISO 8601 basic format
x-amz-content-sha256UNSIGNED-PAYLOAD
Content-LengthFile size in bytes
Content-TypeMIME type (default: application/octet-stream)
x-amz-security-tokenSession token (only if provided)
x-amz-storage-classStorage class (only if provided)
AuthorizationAWS4-HMAC-SHA256 Credential=.../s3/aws4_request, SignedHeaders=..., Signature=...
IHttpTransferManager manager; // injected
var request = new AwsS3UploadRequest("/path/to/photo.jpg")
.WithBucket("my-uploads-bucket", "eu-west-1")
.WithObjectKey("photos/2026/vacation.jpg")
.WithCredentials(accessKeyId, secretAccessKey, sessionToken)
.WithContentType("image/jpeg")
.WithStorageClass("STANDARD_IA")
.WithMeteredConnection()
.Build();
var transfer = await manager.Queue(request);

Use HttpTransferDelegate to handle credential expiration during long uploads.

public partial class MyTransferDelegate(
ILogger<MyTransferDelegate> logger,
IHttpTransferManager manager,
IAwsCredentialService credentialService
) : HttpTransferDelegate(logger, manager, maxErrorRetries: 3)
{
protected override async Task<HttpTransferRequest?> OnAuthorizationFailed(
HttpTransferRequest request,
int retries
)
{
if (retries > 3)
return null; // give up
var creds = await credentialService.GetFreshCredentialsAsync();
// Rebuild the request with fresh credentials
return new AwsS3UploadRequest(request.LocalFilePath)
.WithBucket("my-bucket", "us-east-1")
.WithObjectKey(request.Uri.Split('/').Last())
.WithCredentials(creds.AccessKeyId, creds.SecretAccessKey, creds.SessionToken)
.Build();
}
}

The WithCustomUri() method supports S3-compatible services like MinIO, LocalStack, or DigitalOcean Spaces.

var request = new AwsS3UploadRequest("/path/to/file.pdf")
.WithCustomUri("https://minio.local:9000/my-bucket/file.pdf")
.WithBucket("my-bucket", "us-east-1")
.WithCredentials(accessKeyId, secretAccessKey)
.Build();