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; namespace Marco.Pms.Services.Service { public class S3UploadService { private readonly IAmazonS3 _s3Client; private readonly string _bucketName = "your-bucket-name"; private readonly ILoggingService _logger; private readonly IConfiguration _configuration; public S3UploadService(IOptions 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 UploadFileAsync(string fileName, string contentType) public async Task UploadFileAsync(string base64Data, Guid tenantId, string tag) { byte[] fileBytes; //If base64 has a data URI prefix, strip it var base64 = base64Data.Contains(",") ? base64Data.Substring(base64Data.IndexOf(",") + 1) : base64Data; var allowedFilesType = _configuration.GetSection("WhiteList:ContentType") .GetChildren() .Select(x => x.Value) .ToList(); string fileType = GetContentTypeFromBase64(base64); if (allowedFilesType != null && allowedFilesType.Contains(fileType)) { string fileName = GenerateFileName(fileType, tenantId, tag); fileBytes = Convert.FromBase64String(base64); using var fileStream = new MemoryStream(fileBytes); // Generate a unique object key (you can customize this) var objectKey = $"{fileName}"; 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"); return objectKey; } catch (Exception ex) { _logger.LogError("{error} while uploading file to S3", ex.Message); return string.Empty; } } throw new InvalidOperationException("Unsupported file type."); } public string GeneratePreSignedUrlAsync(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("{error} while requesting presigned url from Amazon S3", ex.Message); return string.Empty; } } public async Task 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("{error} while deleting from Amazon S3", ex.Message); return false; } } public string GenerateFileName(string contentType, Guid tenantId, string? name) { string extenstion = GetExtensionFromMimeType(contentType); if (string.IsNullOrEmpty(name)) return $"{tenantId}_{DateTime.UtcNow:yyyyMMddHHmmssfff}{extenstion}"; return $"{name}_{tenantId}_{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.LogError("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) { // Handle cases where the input string is not valid Base64 _logger.LogError("Invalid Base64 string."); return string.Empty; } catch (Exception ex) { // Handle other potential errors during decoding or inspection _logger.LogError($"An error occurred: {ex.Message}"); return string.Empty; } } } }