155 lines
5.5 KiB
C#
155 lines
5.5 KiB
C#
using Azure;
|
|
using Azure.Storage.Blobs;
|
|
using Azure.Storage.Blobs.Models;
|
|
using Hutopy.Infrastructure.BlobStorage.Contracts;
|
|
|
|
namespace Hutopy.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<AzureBlobStorage> _logger;
|
|
|
|
public AzureBlobStorage(IConfiguration configuration, ILogger<AzureBlobStorage> logger)
|
|
{
|
|
_logger = logger;
|
|
var connectionString = configuration.GetConnectionString("AzureBlob");
|
|
_blobServiceClient = new BlobServiceClient(connectionString);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Upload a file to microsoft azure blob storage.
|
|
/// </summary>
|
|
/// <param name="containerName">The name of the container where the file is stored.</param>
|
|
/// <param name="blobName">The blob name (path within the container, include the file name).</param>
|
|
/// <param name="stream"></param>
|
|
/// <param name="contentType">The content type.</param>
|
|
/// <param name="ct">The cancellation token</param>
|
|
/// <returns></returns>
|
|
public async Task<string> 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
|
|
var 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
|
|
var blobClient = containerClient.GetBlobClient(blobName);
|
|
|
|
// Define the BlobHttpHeaders to include the content type
|
|
var blobHttpHeaders = new BlobHttpHeaders { ContentType = contentType };
|
|
|
|
// Upload the file
|
|
var response = await blobClient.UploadAsync(
|
|
stream,
|
|
new BlobUploadOptions { HttpHeaders = blobHttpHeaders },
|
|
ct);
|
|
|
|
var 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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Download a file to microsoft's azure blob storage.
|
|
/// </summary>
|
|
/// <param name="blobName">The blob name (path within the container).</param>
|
|
/// <param name="containerName">The name of the container where the file is stored. (users)</param>
|
|
/// <param name="ct">The cancellation token for the request</param>
|
|
/// <returns></returns>
|
|
public async Task<MemoryStream> DownloadFileAsync(
|
|
string containerName,
|
|
string blobName,
|
|
CancellationToken ct = default)
|
|
{
|
|
try
|
|
{
|
|
// Get a reference to a container
|
|
var containerClient = _blobServiceClient.GetBlobContainerClient(containerName);
|
|
|
|
// Get a reference to a blob
|
|
var 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;
|
|
}
|
|
}
|
|
}
|