2023-10-02 13:37:27 +00:00
|
|
|
using System.Collections.Concurrent;
|
|
|
|
using System.Net;
|
|
|
|
using System.Text;
|
|
|
|
using Amazon.Runtime;
|
|
|
|
using Amazon.S3;
|
2023-09-14 12:33:45 +00:00
|
|
|
using Amazon.S3.Model;
|
2023-10-02 13:37:27 +00:00
|
|
|
using InnovEnergy.Lib.S3Utils.DataTypes;
|
2023-09-14 12:33:45 +00:00
|
|
|
using InnovEnergy.Lib.Utils;
|
2023-10-02 13:37:27 +00:00
|
|
|
using S3Bucket = InnovEnergy.Lib.S3Utils.DataTypes.S3Bucket;
|
|
|
|
using S3Region = InnovEnergy.Lib.S3Utils.DataTypes.S3Region;
|
2023-09-14 12:33:45 +00:00
|
|
|
|
|
|
|
namespace InnovEnergy.Lib.S3Utils;
|
|
|
|
|
|
|
|
public static class S3
|
|
|
|
{
|
2023-10-02 13:37:27 +00:00
|
|
|
private static readonly ConcurrentDictionary<S3Region, AmazonS3Client> S3ClientCache = new();
|
2023-09-14 12:33:45 +00:00
|
|
|
|
2023-10-02 13:37:27 +00:00
|
|
|
// QOL method
|
|
|
|
public static S3Bucket Bucket(this S3Region region, String name) => new
|
|
|
|
(
|
|
|
|
Name: name,
|
|
|
|
Region: region
|
|
|
|
);
|
2023-09-14 12:33:45 +00:00
|
|
|
|
2023-10-02 13:37:27 +00:00
|
|
|
// QOL method
|
|
|
|
public static S3Url Path(this S3Bucket bucket, String path) => new
|
|
|
|
(
|
|
|
|
Bucket: bucket,
|
|
|
|
Path: path
|
|
|
|
);
|
|
|
|
|
|
|
|
public static IAsyncEnumerable<S3Url> ListObjects(this S3Bucket bucket) => ListObjects(bucket, null);
|
|
|
|
|
|
|
|
public static IAsyncEnumerable<S3Url> ListObjects(this S3Bucket bucket, String? pathPrefix)
|
|
|
|
{
|
|
|
|
return bucket
|
|
|
|
.Region
|
|
|
|
.GetS3Client()
|
2023-09-14 12:33:45 +00:00
|
|
|
.Paginators
|
2023-10-02 13:37:27 +00:00
|
|
|
.ListObjectsV2(new() { BucketName = bucket.Name, Prefix = pathPrefix })
|
|
|
|
.S3Objects
|
|
|
|
.Select(o => new S3Url(o.Key, bucket));
|
2023-09-14 12:33:45 +00:00
|
|
|
}
|
2023-10-16 09:27:19 +00:00
|
|
|
|
|
|
|
public static async Task<ListBucketsResponse> ListAllBuckets(this S3Region region)
|
|
|
|
{
|
|
|
|
return await region
|
|
|
|
.GetS3Client()
|
|
|
|
.ListBucketsAsync();
|
|
|
|
}
|
2023-10-02 13:37:27 +00:00
|
|
|
|
|
|
|
public static Task<Boolean> PutObject(this S3Url path, String data, Encoding encoding) => path.PutObject(encoding.GetBytes(data));
|
|
|
|
public static Task<Boolean> PutObject(this S3Url path, String data) => path.PutObject(data, Encoding.UTF8);
|
|
|
|
public static Task<Boolean> PutObject(this S3Url path, Byte[] data) => path.PutObject(new MemoryStream(data));
|
|
|
|
|
|
|
|
public static async Task<Boolean> PutObject(this S3Url path, Stream data)
|
2023-09-14 12:33:45 +00:00
|
|
|
{
|
2023-10-02 13:37:27 +00:00
|
|
|
var request = new PutObjectRequest
|
2023-09-14 12:33:45 +00:00
|
|
|
{
|
2023-10-02 13:37:27 +00:00
|
|
|
BucketName = path.Bucket.Name,
|
|
|
|
Key = path.Path,
|
|
|
|
InputStream = data
|
2023-09-14 12:33:45 +00:00
|
|
|
};
|
|
|
|
|
2023-10-02 13:37:27 +00:00
|
|
|
var response = await path
|
|
|
|
.Bucket
|
|
|
|
.Region
|
|
|
|
.GetS3Client()
|
|
|
|
.PutObjectAsync(request);
|
|
|
|
|
|
|
|
return response.HttpStatusCode == HttpStatusCode.OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static Task<String> GetObjectAsString(this S3Url path) => GetObjectAsString(path, Encoding.UTF8);
|
2023-09-14 12:33:45 +00:00
|
|
|
|
2023-10-02 13:37:27 +00:00
|
|
|
public static async Task<String> GetObjectAsString(this S3Url path, Encoding encoding)
|
|
|
|
{
|
|
|
|
await using var stream = await GetObjectAsStream(path);
|
|
|
|
using var reader = new StreamReader(stream, encoding);
|
2023-09-14 12:33:45 +00:00
|
|
|
return await reader.ReadToEndAsync();
|
|
|
|
}
|
2023-10-02 13:37:27 +00:00
|
|
|
|
|
|
|
public static async Task<Stream> GetObjectAsStream(this S3Url path)
|
2023-09-14 12:33:45 +00:00
|
|
|
{
|
|
|
|
var request = new GetObjectRequest
|
|
|
|
{
|
2023-10-02 13:37:27 +00:00
|
|
|
BucketName = path.Bucket.Name,
|
|
|
|
Key = path.Path
|
2023-09-14 12:33:45 +00:00
|
|
|
};
|
|
|
|
|
2023-10-02 13:37:27 +00:00
|
|
|
var response = await path
|
|
|
|
.Bucket
|
|
|
|
.Region
|
|
|
|
.GetS3Client()
|
|
|
|
.GetObjectAsync(request);
|
|
|
|
|
|
|
|
return response.ResponseStream;
|
|
|
|
}
|
2023-10-23 11:08:09 +00:00
|
|
|
|
|
|
|
// public static async Task<Boolean> CheckRoleExists(this S3Region region, String roleId)
|
|
|
|
// {
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// var response = await region
|
|
|
|
// .GetIamClient()
|
|
|
|
// .GetRoleAsync(new GetRoleRequest(){RoleName = roleId});
|
|
|
|
// return response.HttpStatusCode != HttpStatusCode.NotFound;
|
|
|
|
// }
|
2023-10-02 13:37:27 +00:00
|
|
|
|
|
|
|
public static async Task<IReadOnlyList<Byte>> 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<String> GetObjectLineByLine(this S3Url url) => GetObjectLineByLine(url, Encoding.UTF8);
|
|
|
|
|
|
|
|
public static async IAsyncEnumerable<String> GetObjectLineByLine(this S3Url url, Encoding encoding)
|
|
|
|
{
|
|
|
|
await using var stream = await url.GetObjectAsStream();
|
|
|
|
|
|
|
|
using var reader = new StreamReader(stream, encoding);
|
2023-09-14 12:33:45 +00:00
|
|
|
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
var line = await reader.ReadLineAsync();
|
2023-10-02 13:37:27 +00:00
|
|
|
|
2023-09-14 12:33:45 +00:00
|
|
|
if (line is not null)
|
|
|
|
yield return line;
|
|
|
|
else
|
|
|
|
yield break;
|
|
|
|
}
|
|
|
|
}
|
2023-10-02 13:37:27 +00:00
|
|
|
|
|
|
|
public static async Task<S3Bucket?> 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<Boolean> 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<Boolean> 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()
|
|
|
|
{
|
2023-10-09 12:23:31 +00:00
|
|
|
ServiceURL = region.Name.EnsureStartsWith("https://"),
|
2023-10-02 13:37:27 +00:00
|
|
|
ForcePathStyle = true,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
2023-09-14 12:33:45 +00:00
|
|
|
}
|