97 lines
3.4 KiB
C#
97 lines
3.4 KiB
C#
using Amazon.Runtime;
|
|
using Amazon.S3;
|
|
using Amazon.S3.Model;
|
|
|
|
namespace TheCr3w.Shared.Storage;
|
|
|
|
public class R2StorageOptions
|
|
{
|
|
public string Endpoint { get; set; } = "https://bf705752e38169fedb83efd5b747390e.r2.cloudflarestorage.com";
|
|
public string BucketName { get; set; } = "thecr3w-app";
|
|
public string Region { get; set; } = "auto";
|
|
public string AccessKeyId { get; set; } = string.Empty;
|
|
public string SecretAccessKey { get; set; } = string.Empty;
|
|
}
|
|
|
|
public interface IR2StorageService
|
|
{
|
|
string GeneratePreSignedDownloadUrl(string objectKey, TimeSpan? duration = null);
|
|
string GeneratePreSignedUploadUrl(string objectKey, string contentType, TimeSpan? duration = null);
|
|
Task<bool> ObjectExistsAsync(string objectKey, CancellationToken cancellationToken = default);
|
|
}
|
|
|
|
public class R2StorageService : IR2StorageService
|
|
{
|
|
private readonly R2StorageOptions _options;
|
|
private readonly IAmazonS3 _s3Client;
|
|
|
|
public R2StorageService(R2StorageOptions options)
|
|
{
|
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
|
|
|
var credentials = new BasicAWSCredentials(_options.AccessKeyId, _options.SecretAccessKey);
|
|
var config = new AmazonS3Config
|
|
{
|
|
ServiceURL = _options.Endpoint,
|
|
AuthenticationRegion = _options.Region,
|
|
ForcePathStyle = true
|
|
};
|
|
|
|
_s3Client = new AmazonS3Client(credentials, config);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generuje krótko żyjący (domyślnie 15 minut) podpisany adres URL (Pre-signed S3 GET)
|
|
/// pozwalający na bezpośrednie pobranie artefaktu z Cloudflare R2 bez obciążania serwera.
|
|
/// </summary>
|
|
public string GeneratePreSignedDownloadUrl(string objectKey, TimeSpan? duration = null)
|
|
{
|
|
var expires = DateTime.UtcNow.Add(duration ?? TimeSpan.FromMinutes(15));
|
|
var request = new GetPreSignedUrlRequest
|
|
{
|
|
BucketName = _options.BucketName,
|
|
Key = objectKey,
|
|
Verb = HttpVerb.GET,
|
|
Expires = expires
|
|
};
|
|
|
|
return _s3Client.GetPreSignedURL(request);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Generuje krótko żyjący (domyślnie 30 minut) podpisany adres URL (Pre-signed S3 PUT)
|
|
/// pozwalający na bezpieczny upload pliku do Cloudflare R2.
|
|
/// </summary>
|
|
public string GeneratePreSignedUploadUrl(string objectKey, string contentType, TimeSpan? duration = null)
|
|
{
|
|
var expires = DateTime.UtcNow.Add(duration ?? TimeSpan.FromMinutes(30));
|
|
var request = new GetPreSignedUrlRequest
|
|
{
|
|
BucketName = _options.BucketName,
|
|
Key = objectKey,
|
|
Verb = HttpVerb.PUT,
|
|
Expires = expires,
|
|
ContentType = contentType
|
|
};
|
|
|
|
return _s3Client.GetPreSignedURL(request);
|
|
}
|
|
|
|
public async Task<bool> ObjectExistsAsync(string objectKey, CancellationToken cancellationToken = default)
|
|
{
|
|
try
|
|
{
|
|
var response = await _s3Client.GetObjectMetadataAsync(new GetObjectMetadataRequest
|
|
{
|
|
BucketName = _options.BucketName,
|
|
Key = objectKey
|
|
}, cancellationToken);
|
|
|
|
return response.HttpStatusCode == System.Net.HttpStatusCode.OK;
|
|
}
|
|
catch (AmazonS3Exception ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|