260 lines
8.9 KiB
C#
260 lines
8.9 KiB
C#
using Amazon.S3;
|
|
using Amazon.S3.Model;
|
|
using Amazon.S3.Transfer;
|
|
using Marco.Pms.Model.Utilities;
|
|
using MarcoBMS.Services.Service;
|
|
using Microsoft.Extensions.Options;
|
|
using MimeDetective;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Marco.Pms.Services.Service
|
|
{
|
|
|
|
public class S3UploadService
|
|
{
|
|
private readonly IAmazonS3 _s3Client;
|
|
private readonly string _bucketName;
|
|
private readonly ILoggingService _logger;
|
|
private readonly IConfiguration _configuration;
|
|
|
|
public S3UploadService(IOptions<AWSSettings> awsOptions, ILoggingService logger, IConfiguration configuration)
|
|
{
|
|
_logger = logger;
|
|
_configuration = configuration;
|
|
var settings = awsOptions.Value;
|
|
|
|
var region = Amazon.RegionEndpoint.GetBySystemName(settings.Region);
|
|
_bucketName = settings.BucketName;
|
|
|
|
_s3Client = new AmazonS3Client(settings.AccessKey, settings.SecretKey, region);
|
|
}
|
|
//public async Task<string> UploadFileAsync(string fileName, string contentType)
|
|
public async Task UploadFileAsync(string base64, string fileType, string objectKey)
|
|
{
|
|
byte[] fileBytes;
|
|
|
|
|
|
var allowedFilesType = _configuration.GetSection("WhiteList:ContentType")
|
|
.GetChildren()
|
|
.Select(x => x.Value)
|
|
.ToList();
|
|
|
|
if (allowedFilesType == null || !allowedFilesType.Contains(fileType))
|
|
{
|
|
_logger.LogWarning("Unsupported file type. {FileType}", fileType);
|
|
throw new InvalidOperationException("Unsupported file type.");
|
|
}
|
|
|
|
fileBytes = Convert.FromBase64String(base64);
|
|
|
|
|
|
using var fileStream = new MemoryStream(fileBytes);
|
|
|
|
var uploadRequest = new TransferUtilityUploadRequest
|
|
{
|
|
InputStream = fileStream,
|
|
Key = objectKey,
|
|
BucketName = _bucketName,
|
|
ContentType = fileType,
|
|
AutoCloseStream = true
|
|
};
|
|
try
|
|
{
|
|
var transferUtility = new TransferUtility(_s3Client);
|
|
await transferUtility.UploadAsync(uploadRequest);
|
|
_logger.LogInfo("File uploaded to Amazon S3");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error ocurred while uploading file to S3", ex.Message);
|
|
}
|
|
|
|
|
|
}
|
|
public string GeneratePreSignedUrl(string objectKey)
|
|
{
|
|
|
|
int expiresInMinutes = 10;
|
|
var request = new GetPreSignedUrlRequest
|
|
{
|
|
BucketName = _bucketName,
|
|
Key = objectKey,
|
|
Expires = DateTime.UtcNow.AddMinutes(expiresInMinutes),
|
|
Verb = HttpVerb.GET // for download
|
|
};
|
|
try
|
|
{
|
|
string url = _s3Client.GetPreSignedURL(request);
|
|
_logger.LogInfo("Requested presigned url from Amazon S3");
|
|
return url;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error occured while requesting presigned url from Amazon S3");
|
|
return string.Empty;
|
|
}
|
|
}
|
|
public async Task<bool> DeleteFileAsync(string objectKey)
|
|
{
|
|
try
|
|
{
|
|
var deleteRequest = new DeleteObjectRequest
|
|
{
|
|
BucketName = _bucketName,
|
|
Key = objectKey
|
|
};
|
|
|
|
var response = await _s3Client.DeleteObjectAsync(deleteRequest);
|
|
_logger.LogInfo("File deleted from Amazon S3");
|
|
return response.HttpStatusCode == System.Net.HttpStatusCode.NoContent;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "while deleting from Amazon S3");
|
|
return false;
|
|
}
|
|
}
|
|
public string GenerateFileName(string contentType, Guid entityId, string? name)
|
|
{
|
|
string extenstion = GetExtensionFromMimeType(contentType);
|
|
|
|
if (string.IsNullOrEmpty(name))
|
|
return $"{entityId}_{DateTime.UtcNow:yyyyMMddHHmmssfff}{extenstion}";
|
|
return $"{name}_{entityId}_{DateTime.UtcNow:yyyyMMddHHmmssfff}{extenstion}";
|
|
|
|
}
|
|
public string GetExtensionFromMimeType(string contentType)
|
|
{
|
|
switch (contentType.ToLowerInvariant())
|
|
{
|
|
case "application/pdf":
|
|
return ".pdf";
|
|
|
|
case "application/msword":
|
|
return ".doc";
|
|
|
|
case "application/vnd.openxmlformats-officedocument.wordprocessingml.document":
|
|
return ".docx";
|
|
|
|
case "application/vnd.ms-excel":
|
|
return ".xls";
|
|
|
|
case "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet":
|
|
return ".xlsx";
|
|
|
|
case "application/mspowerpoint":
|
|
return ".ppt";
|
|
|
|
case "application/vnd.openxmlformats-officedocument.presentationml.presentation":
|
|
return ".pptx";
|
|
|
|
case "text/plain":
|
|
return ".txt";
|
|
|
|
case "application/rtf":
|
|
return ".rtf";
|
|
|
|
case "image/jpeg":
|
|
return ".jpg";
|
|
|
|
case "image/png":
|
|
return ".png";
|
|
|
|
case "image/gif":
|
|
return ".gif";
|
|
|
|
case "image/bmp":
|
|
return ".bmp";
|
|
|
|
case "text/csv":
|
|
return ".csv";
|
|
|
|
default:
|
|
return ""; // or ".bin", or throw an error, based on your needs
|
|
}
|
|
}
|
|
public string GetContentTypeFromBase64(string base64String)
|
|
{
|
|
if (string.IsNullOrEmpty(base64String))
|
|
{
|
|
return string.Empty; // Or handle the empty case as needed
|
|
}
|
|
|
|
try
|
|
{
|
|
// 1. Decode the Base64 string to a byte array
|
|
byte[] decodedBytes = Convert.FromBase64String(base64String);
|
|
|
|
// 2. Create a ContentInspector (using default definitions for this example)
|
|
var inspector = new ContentInspectorBuilder()
|
|
{
|
|
Definitions = MimeDetective.Definitions.DefaultDefinitions.All()
|
|
}.Build();
|
|
|
|
// 3. Inspect the byte array to determine the content type
|
|
var results = inspector.Inspect(decodedBytes);
|
|
|
|
if (results.Any())
|
|
{
|
|
var bestMatch = results
|
|
.OrderByDescending(r => r.Points)
|
|
.FirstOrDefault();
|
|
if (bestMatch?.Definition?.File?.MimeType != null)
|
|
{
|
|
return bestMatch.Definition.File.MimeType;
|
|
}
|
|
else
|
|
{
|
|
_logger.LogWarning("Warning: Could not find MimeType, Type, or ContentType property in Definition.");
|
|
return "application/octet-stream";
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return "application/octet-stream"; // Default if type cannot be determined
|
|
}
|
|
}
|
|
catch (FormatException fEx)
|
|
{
|
|
// Handle cases where the input string is not valid Base64
|
|
_logger.LogError(fEx, "Invalid Base64 string.");
|
|
return string.Empty;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Handle other potential errors during decoding or inspection
|
|
_logger.LogError(ex, "An error occurred while decoding base64");
|
|
return string.Empty;
|
|
}
|
|
}
|
|
public bool IsBase64String(string? input)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(input))
|
|
return false;
|
|
|
|
// Normalize string
|
|
input = input.Trim();
|
|
|
|
// Length must be multiple of 4
|
|
if (input.Length % 4 != 0)
|
|
return false;
|
|
|
|
// Valid Base64 characters with correct padding
|
|
var base64Regex = new Regex(@"^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$");
|
|
if (!base64Regex.IsMatch(input))
|
|
return false;
|
|
|
|
try
|
|
{
|
|
// Decode and re-encode to confirm validity
|
|
var bytes = Convert.FromBase64String(input);
|
|
var reEncoded = Convert.ToBase64String(bytes);
|
|
return input == reEncoded;
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
} |