using System.Collections.Concurrent; using System.Net; using System.Text; using Amazon.Runtime; using Amazon.S3; using Amazon.S3.Model; using InnovEnergy.Lib.S3Utils.DataTypes; using InnovEnergy.Lib.Utils; using S3Bucket = InnovEnergy.Lib.S3Utils.DataTypes.S3Bucket; using S3Region = InnovEnergy.Lib.S3Utils.DataTypes.S3Region; namespace InnovEnergy.Lib.S3Utils; public static class S3 { private static readonly ConcurrentDictionary S3ClientCache = new(); // QOL method public static S3Bucket Bucket(this S3Region region, String name) => new ( Name: name, Region: region ); // QOL method public static S3Url Path(this S3Bucket bucket, String path) => new ( Bucket: bucket, Path: path ); public static IAsyncEnumerable ListObjects(this S3Bucket bucket) => ListObjects(bucket, null); public static IAsyncEnumerable ListObjects(this S3Bucket bucket, String? pathPrefix) { return bucket .Region .GetS3Client() .Paginators .ListObjectsV2(new() { BucketName = bucket.Name, Prefix = pathPrefix }) .S3Objects .Select(o => new S3Url(o.Key, bucket)); } public static Task PutObject(this S3Url path, String data, Encoding encoding) => path.PutObject(encoding.GetBytes(data)); public static Task PutObject(this S3Url path, String data) => path.PutObject(data, Encoding.UTF8); public static Task PutObject(this S3Url path, Byte[] data) => path.PutObject(new MemoryStream(data)); public static async Task PutObject(this S3Url path, Stream data) { var request = new PutObjectRequest { BucketName = path.Bucket.Name, Key = path.Path, InputStream = data }; var response = await path .Bucket .Region .GetS3Client() .PutObjectAsync(request); return response.HttpStatusCode == HttpStatusCode.OK; } public static Task GetObjectAsString(this S3Url path) => GetObjectAsString(path, Encoding.UTF8); public static async Task GetObjectAsString(this S3Url path, Encoding encoding) { await using var stream = await GetObjectAsStream(path); using var reader = new StreamReader(stream, encoding); return await reader.ReadToEndAsync(); } public static async Task GetObjectAsStream(this S3Url path) { var request = new GetObjectRequest { BucketName = path.Bucket.Name, Key = path.Path }; var response = await path .Bucket .Region .GetS3Client() .GetObjectAsync(request); return response.ResponseStream; } public static async Task> GetObject(this S3Url url) { // beautiful await using stream soup... await using var stream = await url.GetObjectAsStream(); using var memoryStream = new MemoryStream(); await stream.CopyToAsync(memoryStream); return memoryStream.ToArray(); } public static IAsyncEnumerable GetObjectLineByLine(this S3Url url) => GetObjectLineByLine(url, Encoding.UTF8); public static async IAsyncEnumerable GetObjectLineByLine(this S3Url url, Encoding encoding) { await using var stream = await url.GetObjectAsStream(); using var reader = new StreamReader(stream, encoding); while (true) { var line = await reader.ReadLineAsync(); if (line is not null) yield return line; else yield break; } } public static async Task PutBucket(this S3Region region, String name) { var request = new PutBucketRequest { BucketName = name }; var response = await region .GetS3Client() .PutBucketAsync(request); return response.HttpStatusCode switch { HttpStatusCode.OK => region.Bucket(name), _ => null }; } public static async Task PutCors(this S3Bucket bucket, CORSConfiguration corsConfiguration) { var request = new PutCORSConfigurationRequest { BucketName = bucket.Name, Configuration = corsConfiguration }; var response = await bucket .GetS3Client() .PutCORSConfigurationAsync(request); return response.HttpStatusCode == HttpStatusCode.OK; } public static async Task DeleteBucket(this S3Bucket bucket) { var request = new DeleteBucketRequest { BucketName = bucket.Name }; var response = await bucket .GetS3Client() .DeleteBucketAsync(request); return response.HttpStatusCode == HttpStatusCode.OK; } private static AmazonS3Client GetS3Client(this S3Url url ) => url.Bucket.GetS3Client(); private static AmazonS3Client GetS3Client(this S3Bucket bucket) => bucket.Region.GetS3Client(); private static AmazonS3Client GetS3Client(this S3Region region) { return S3ClientCache.GetOrAdd(region, CreateS3Client); // Memoize } private static AmazonS3Client CreateS3Client(S3Region region) => new ( credentials: new BasicAWSCredentials(region.Credentials.Key, region.Credentials.Secret), clientConfig: new() { ServiceURL = region.Name.EnsureStartsWith("https://"), ForcePathStyle = true, } ); }