using Amazon.S3.Model;
using Amazon.S3.Transfer;
using Amazon.S3.Wrapper.Enums;
using Amazon.S3.Wrapper.Exceptions;
using Amazon.S3.Wrapper.Extensions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Amazon.S3.Wrapper
{
public class S3Client : IS3Client
{
private readonly S3StorageClass _storageClass;
private readonly ContentType _contentType;
private readonly string _bucketName;
private readonly IAmazonS3 _client;
///
/// Creates a new instance of amazon s3 client
///
/// Client for accessing S3
/// Bucket name
/// Default value xml
/// Default value S3StorageClass.Standard
public S3Client(IAmazonS3 amazonS3, string bucketName, ContentType? contentType, S3StorageClass storageClass)
{
if (string.IsNullOrWhiteSpace(bucketName))
{
throw new ArgumentNullException(nameof(bucketName));
}
_client = amazonS3;
_bucketName = bucketName;
_contentType = contentType ?? ContentType.Json;
_storageClass = storageClass ?? S3StorageClass.Standard;
}
public S3Client(string region, string bucketName, ContentType? contentType, S3StorageClass storageClass)
{
if (string.IsNullOrWhiteSpace(region))
{
throw new ArgumentNullException(nameof(region));
}
if (string.IsNullOrWhiteSpace(bucketName))
{
throw new ArgumentNullException(nameof(bucketName));
}
var s3Config = new AmazonS3Config
{
RegionEndpoint = RegionEndpoint.GetBySystemName(region),
MaxErrorRetry = 1,
};
_client = new AmazonS3Client(s3Config);
_bucketName = bucketName;
_contentType = contentType ?? ContentType.Json;
_storageClass = storageClass ?? S3StorageClass.Standard;
}
///
/// Upload a object as multipart upload to s3 bucket defined in initialization
///
/// This key is used to identify the object in S3
/// content to be uploaded
/// PutObjectResponse object
public async Task UploadObjectAsync(string key, Stream content, CancellationToken cancellationToken = default)
{
var result = await UploadObjectAsync(key, content, _contentType, _storageClass, cancellationToken).ConfigureAwait(false);
return result;
}
///
/// Upload a object to s3 bucket using Multipart upload
///
/// This key is used to identify the object in S3
/// content to be uploaded
/// Overrides contentype provided in registry
/// Overrides storageClass provided in registry
///
public async Task UploadObjectAsync(string key, Stream content, ContentType? contentType = null, S3StorageClass storageClass = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(key))
{
throw new ArgumentNullException(nameof(key));
}
var requestStorageClass = storageClass ?? _storageClass;
var requestContentType = contentType ?? _contentType;
var transferUtility =
new TransferUtility(_client);
try
{
var request = new TransferUtilityUploadRequest
{
BucketName = _bucketName,
Key = key,
StorageClass = requestStorageClass,
ContentType = requestContentType.Value(),
InputStream = content
};
await transferUtility.UploadAsync(request, cancellationToken);
return true;
}
catch (Exception e)
{
var errorMessage = $"Error encountered {e.Message}.{Environment.NewLine}" +
$"It was not possible to upload the object.{Environment.NewLine}" +
$"BucketName: {_bucketName}{Environment.NewLine}" +
$"Key: {key}{Environment.NewLine}" +
$"ContentBody: {content}{Environment.NewLine}";
//TODO: Try to abort the upload using upload id
await transferUtility.AbortMultipartUploadsAsync(_bucketName, DateTime.Now);
throw new AmazonS3ClientException(errorMessage, e);
}
}
///
/// Upload a object to s3 bucket defined in initialization
///
/// This key is used to identify the object in S3
/// content to be uploaded
/// PutObjectResponse object
public async Task PutObjectAsync(string key, string content, CancellationToken cancellationToken = default)
{
var result = await PutObjectAsync(key, content, _contentType, _storageClass, cancellationToken).ConfigureAwait(false);
return result;
}
///
/// Upload a object to s3 bucket
///
/// This key is used to identify the object in S3
/// content to be uploaded
/// Overrides contentype provided in registry
/// Overrides storageClass provided in registry
///
public async Task PutObjectAsync(string key, string content, ContentType? contentType = null, S3StorageClass storageClass = null, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(key))
{
throw new ArgumentNullException(nameof(key));
}
if (string.IsNullOrWhiteSpace(content))
{
throw new ArgumentNullException(nameof(content));
}
var requestStorageClass = storageClass ?? _storageClass;
var requestContentType = contentType ?? _contentType;
try
{
var putRequest = new PutObjectRequest
{
BucketName = _bucketName,
Key = key,
StorageClass = requestStorageClass,
ContentType = requestContentType.Value(),
ContentBody = content
};
var result = await _client.PutObjectAsync(putRequest, cancellationToken).ConfigureAwait(false);
return result;
}
catch (Exception e)
{
var errorMessage = $"Error encountered {e.Message}.{Environment.NewLine}" +
$"It was not possible to upload the object.{Environment.NewLine}" +
$"BucketName: {_bucketName}{Environment.NewLine}" +
$"Key: {key}{Environment.NewLine}" +
$"StorageClass: {requestStorageClass}{Environment.NewLine}" +
$"ContentType: {requestContentType}{Environment.NewLine}" +
$"ContentBody: {content}{Environment.NewLine}";
throw new AmazonS3ClientException(errorMessage, e);
}
}
///
/// Get object from s3 bucket
///
/// This key is used to identify the object in S3
///
public async Task GetObjectAsync(string key, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(key))
{
throw new ArgumentNullException(nameof(key));
}
try
{
var request = new GetObjectRequest
{
BucketName = _bucketName,
Key = key
};
var result = await _client.GetObjectAsync(request, cancellationToken).ConfigureAwait(false);
return result;
}
catch (Exception e)
{
var errorMessage = $"Error encountered {e.Message}.{Environment.NewLine}" +
$"It was not possible to get the object.{Environment.NewLine}" +
$"BucketName: {_bucketName}{Environment.NewLine}" +
$"Key: {key}{Environment.NewLine}";
throw new AmazonS3ClientException(errorMessage, e);
}
}
///
/// Delete a file object from bucket.
///
/// This key is used to identify the object in S3
/// Propagates notification that operations should be canceled.
/// GetObjectResponse
public async Task DeleteObjectAsync(string key, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(key))
{
throw new ArgumentNullException(nameof(key));
}
try
{
var deleteObjectRequest = new DeleteObjectRequest
{
BucketName = _bucketName,
Key = key
};
var deleteResult = await _client.DeleteObjectAsync(deleteObjectRequest, cancellationToken).ConfigureAwait(false);
return deleteResult;
}
catch (Exception e)
{
var errorMessage = $"Error encountered {e.Message}.{Environment.NewLine}" +
$"It was not possible to delete the object.{Environment.NewLine}" +
$"BucketName: {_bucketName}{Environment.NewLine}" +
$"Key: {key}{Environment.NewLine}";
throw new AmazonS3ClientException(errorMessage, e);
}
}
///
/// Delete a list of objects from bucket.
///
/// List of object keys to delete.
/// Propagates notification that operations should be canceled.
/// DeleteObjectsResponse
public async Task DeleteObjectsAsync(IEnumerable keys, CancellationToken cancellationToken = default)
{
if (keys.IsNullOrEmpty())
{
throw new ArgumentNullException(nameof(keys));
}
if (keys.Any(key => string.IsNullOrWhiteSpace(key)))
{
throw new Exception("Element keys cannot be null or empty.");
}
try
{
var deleteObjectsRequest = new DeleteObjectsRequest
{
BucketName = _bucketName,
Objects = new List(keys.Select(key => new KeyVersion() { Key = key }))
};
var deleteResult = await _client.DeleteObjectsAsync(deleteObjectsRequest, cancellationToken).ConfigureAwait(false);
return deleteResult;
}
catch (Exception e)
{
var errorMessage = $"Error encountered {e.Message}.{Environment.NewLine}" +
$"It was not possible to delete the objects.{Environment.NewLine}" +
$"BucketName: {_bucketName}{Environment.NewLine}" +
$"Key: {string.Join(",", keys)}{Environment.NewLine}";
throw new AmazonS3ClientException(errorMessage, e);
}
}
///
/// Check if the file exists or not.
///
/// List of object keys to delete.
/// Propagates notification that operations should be canceled.
/// DeleteObjectsResponse
public async Task IsFileExistsAsync(string fileName, string versionId)
{
try
{
GetObjectMetadataRequest request = new GetObjectMetadataRequest()
{
BucketName = _bucketName,
Key = fileName,
VersionId = !string.IsNullOrEmpty(versionId) ? versionId : null
};
var response = await _client.GetObjectMetadataAsync(request);
return true;
}
catch (Exception ex)
{
if (ex.InnerException != null && ex.InnerException is AmazonS3Exception awsEx)
{
if (string.Equals(awsEx.ErrorCode, "NoSuchBucket"))
return false;
else if (string.Equals(awsEx.ErrorCode, "NotFound"))
return false;
}
throw;
}
}
}
}