using Azure; using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; using Socialize.Infrastructure.BlobStorage.Contracts; namespace Socialize.Infrastructure.BlobStorage.Services; public class AzureBlobStorage : IBlobStorage { private const long MaxUploadSize = 10 * 1024 * 1024; // 10 MB in bytes private readonly BlobServiceClient _blobServiceClient; private readonly ILogger _logger; public AzureBlobStorage(IConfiguration configuration, ILogger logger) { _logger = logger; string? connectionString = configuration.GetConnectionString("AzureBlob"); _blobServiceClient = new BlobServiceClient(connectionString); } /// /// Upload a file to microsoft azure blob storage. /// /// The name of the container where the file is stored. /// The blob name (path within the container, include the file name). /// /// The content type. /// The cancellation token /// public async Task UploadFileAsync( string containerName, string blobName, Stream stream, string contentType, CancellationToken ct = default) { // Read the file stream into a memory stream to determine the length // WATCH FOR MEMORY USAGE USING THE MEMORY STREAM. stream.Position = 0; // Check if the file size exceeds the maximum upload size if (stream.Length > MaxUploadSize) { _logger.LogError( $"Blob storage: File size exceeds the maximum allowed size of {MaxUploadSize} bytes."); throw new InvalidOperationException( $"Blob storage: File size exceeds the maximum allowed size of {MaxUploadSize} bytes."); } // Validate content type if (!ContentTypes.IsAllowed(contentType, stream)) { _logger.LogError( $"Blob storage: Unsupported file type {contentType}."); throw new InvalidOperationException("Unsupported file type."); } try { // Get a reference to a container BlobContainerClient? containerClient = _blobServiceClient.GetBlobContainerClient(containerName); // Create the container if it does not exist await containerClient.CreateIfNotExistsAsync( PublicAccessType.Blob, cancellationToken: ct); // Get a reference to a blob BlobClient? blobClient = containerClient.GetBlobClient(blobName); // Define the BlobHttpHeaders to include the content type BlobHttpHeaders blobHttpHeaders = new() { ContentType = contentType }; // Upload the file Response? response = await blobClient.UploadAsync( stream, new BlobUploadOptions { HttpHeaders = blobHttpHeaders }, ct); string fileUri = blobClient.Uri.ToString(); _logger.LogInformation( """ Blob storage: Status [ {ResponseStatus} ] Uploaded [ {BlobName} ] to the container [ {ContainerName} ] with contentType [ {ContentType} ] with a length of [ {StreamLength} bytes ] with the uri [ {FileUri} ] """, response.GetRawResponse().Status.ToString(), blobName, containerName, contentType, stream.Length, fileUri ); // Return the URI of the uploaded blob return fileUri; } catch (RequestFailedException ex) { _logger.LogError($"Blob storage: Azure Storage request failed: {ex.Message}"); throw; } catch (Exception ex) { _logger.LogError($"Blob storage: An error occurred: {ex.Message}"); throw; } } /// /// Download a file to microsoft's azure blob storage. /// /// The blob name (path within the container). /// The name of the container where the file is stored. (users) /// The cancellation token for the request /// public async Task DownloadFileAsync( string containerName, string blobName, CancellationToken ct = default) { try { // Get a reference to a container BlobContainerClient? containerClient = _blobServiceClient.GetBlobContainerClient(containerName); // Get a reference to a blob BlobClient? blobClient = containerClient.GetBlobClient(blobName); // Download the blob to a stream BlobDownloadInfo download = await blobClient.DownloadAsync(ct); MemoryStream memoryStream = new(); await download.Content.CopyToAsync(memoryStream, ct); memoryStream.Position = 0; // Ensure the stream is at the beginning return memoryStream; } catch (RequestFailedException ex) { _logger.LogError($"Azure Storage request failed: {ex.Message}"); throw; } catch (Exception ex) { _logger.LogError($"An error occurred: {ex.Message}"); throw; } } }