Add project files.
This commit is contained in:
		
							
								
								
									
										372
									
								
								Core.Blueprint.Storage/Provider/BlobStorageProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										372
									
								
								Core.Blueprint.Storage/Provider/BlobStorageProvider.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,372 @@ | ||||
| using Azure; | ||||
| using Azure.Storage.Blobs; | ||||
| using Azure.Storage.Blobs.Models; | ||||
| using Azure.Storage.Blobs.Specialized; | ||||
| using Azure.Storage.Sas; | ||||
| using Core.Blueprint.Storage.Adapters; | ||||
| using Core.Blueprint.Storage.Contracts; | ||||
| using Microsoft.Extensions.Configuration; | ||||
|  | ||||
| namespace Core.Blueprint.Storage.Provider | ||||
| { | ||||
|     public sealed class BlobStorageProvider : IBlobStorageProvider | ||||
|     { | ||||
|         private readonly BlobServiceClient _blobServiceClient; | ||||
|         private readonly BlobContainerClient _blobContainerClient; | ||||
|         private readonly string _containerName; | ||||
|         private readonly Trie _trie = new Trie(); | ||||
|  | ||||
|         public BlobStorageProvider(BlobServiceClient blobServiceClient, IConfiguration configuration) | ||||
|         { | ||||
|             _blobServiceClient = blobServiceClient; | ||||
|             _containerName = configuration.GetSection("BlobStorage:ContainerName").Value ?? ""; | ||||
|  | ||||
|             if (string.IsNullOrEmpty(_containerName)) | ||||
|                 throw new ArgumentException("Blob container cannot be null or empty."); | ||||
|  | ||||
|             _blobContainerClient = blobServiceClient.GetBlobContainerClient(_containerName); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Creates the blob container if it does not exist. | ||||
|         /// </summary> | ||||
|         public async Task<Response<BlobContainerInfo>> CreateIfNotExistsAsync() | ||||
|         { | ||||
|             return await _blobContainerClient.CreateIfNotExistsAsync(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Deletes the blob container if it exists. | ||||
|         /// </summary> | ||||
|         public async Task DeleteIfExistsAsync() | ||||
|         { | ||||
|             await _blobContainerClient.DeleteIfExistsAsync(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets properties of the blob container. | ||||
|         /// </summary> | ||||
|         public async Task<Response<BlobContainerProperties>> GetPropertiesAsync() | ||||
|         { | ||||
|             return await _blobContainerClient.GetPropertiesAsync(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Sets metadata for the blob container. | ||||
|         /// </summary> | ||||
|         public async Task SetMetadataAsync(IDictionary<string, string> metadata) | ||||
|         { | ||||
|             await _blobContainerClient.SetMetadataAsync(metadata); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Uploads a blob to the container. | ||||
|         /// </summary> | ||||
|         public async Task<Response<BlobContentInfo>> UploadBlobAsync(string blobName, Stream content) | ||||
|         { | ||||
|             var blobClient = _blobContainerClient.GetBlobClient(blobName); | ||||
|             return await blobClient.UploadAsync(content, overwrite: true); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Downloads a blob from the container. | ||||
|         /// </summary> | ||||
|         public async Task<Response<BlobDownloadInfo>> DownloadBlobAsync(string blobName) | ||||
|         { | ||||
|             var blobClient = _blobContainerClient.GetBlobClient(blobName); | ||||
|             return await blobClient.DownloadAsync(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Deletes a blob from the container. | ||||
|         /// </summary> | ||||
|         public async Task<bool> DeleteBlobAsync(string blobName) | ||||
|         { | ||||
|             var blobClient = _blobContainerClient.GetBlobClient(blobName); | ||||
|             return await blobClient.DeleteIfExistsAsync(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Lists all blobs in the container with an optional prefix. | ||||
|         /// </summary> | ||||
|         public async Task<IEnumerable<BlobItem>> ListBlobItemAsync(string? prefix = null) | ||||
|         { | ||||
|             var blobs = new List<BlobItem>(); | ||||
|  | ||||
|             await foreach (var blobItem in _blobContainerClient.GetBlobsAsync(prefix: prefix)) | ||||
|             { | ||||
|                 blobs.Add(blobItem); | ||||
|             } | ||||
|  | ||||
|             return blobs; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Retrieves the account information for the associated Blob Service Client. | ||||
|         /// </summary> | ||||
|         /// <param name="cancellation"> | ||||
|         /// A <see cref="CancellationToken"/> that can be used to cancel the operation. | ||||
|         /// </param> | ||||
|         /// <returns> | ||||
|         /// A task that represents the asynchronous operation. The task result contains the | ||||
|         /// <see cref="AccountInfo"/> object, which provides details about the account, such as the SKU | ||||
|         /// and account kind. | ||||
|         public async Task<AccountInfo> GetAccountInfoAsync(CancellationToken cancellation) | ||||
|         { | ||||
|             return await _blobServiceClient.GetAccountInfoAsync(cancellation); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Gets a blob client for a specific blob. | ||||
|         /// </summary> | ||||
|         public BlobClient GetBlobClient(string blobName) | ||||
|         { | ||||
|             return _blobContainerClient.GetBlobClient(blobName); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Lists blobs hierarchically using a delimiter. | ||||
|         /// </summary> | ||||
|         public async Task<IEnumerable<BlobHierarchyItem>> ListBlobsByHierarchyAsync(string? prefix = null, string delimiter = "/") | ||||
|         { | ||||
|             var blobs = new List<BlobHierarchyItem>(); | ||||
|  | ||||
|             await foreach (var blobHierarchyItem in _blobContainerClient.GetBlobsByHierarchyAsync(prefix: prefix, delimiter: delimiter)) | ||||
|             { | ||||
|                 blobs.Add(blobHierarchyItem); | ||||
|             } | ||||
|  | ||||
|             return blobs; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Generates a SAS token for the container with specified permissions. | ||||
|         /// </summary> | ||||
|         public Uri GenerateContainerSasUri(BlobContainerSasPermissions permissions, DateTimeOffset expiresOn) | ||||
|         { | ||||
|             if (!_blobContainerClient.CanGenerateSasUri) | ||||
|             { | ||||
|                 throw new InvalidOperationException("Cannot generate SAS URI. Ensure the client is authorized with account key credentials."); | ||||
|             } | ||||
|  | ||||
|             var sasBuilder = new BlobSasBuilder | ||||
|             { | ||||
|                 BlobContainerName = _blobContainerClient.Name, | ||||
|                 Resource = "c", // c for container | ||||
|                 ExpiresOn = expiresOn | ||||
|             }; | ||||
|  | ||||
|             sasBuilder.SetPermissions(permissions); | ||||
|             return _blobContainerClient.GenerateSasUri(sasBuilder); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Acquires a lease on the blob container. | ||||
|         /// </summary> | ||||
|         public async Task<Response<BlobLease>> AcquireLeaseAsync(string? proposedId = null, TimeSpan? duration = null) | ||||
|         { | ||||
|             return await _blobContainerClient.GetBlobLeaseClient(proposedId).AcquireAsync(duration ?? TimeSpan.FromSeconds(60)); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Releases a lease on the blob container. | ||||
|         /// </summary> | ||||
|         public async Task ReleaseLeaseAsync(string leaseId) | ||||
|         { | ||||
|             await _blobContainerClient.GetBlobLeaseClient(leaseId).ReleaseAsync(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Sets access policies for the blob container. | ||||
|         /// </summary> | ||||
|         public async Task SetAccessPolicyAsync(PublicAccessType accessType, IEnumerable<BlobSignedIdentifier>? identifiers = null) | ||||
|         { | ||||
|             await _blobContainerClient.SetAccessPolicyAsync(accessType, identifiers); | ||||
|         } | ||||
|         /// <summary> | ||||
|         /// Lists blobs in the container with an optional prefix. | ||||
|         /// </summary> | ||||
|         public async Task<IEnumerable<BlobFileAdapter>> ListBlobsAsync(string? prefix = null) | ||||
|         { | ||||
|             var blobs = new List<BlobFileAdapter>(); | ||||
|  | ||||
|             await foreach (BlobItem blob in _blobContainerClient.GetBlobsAsync(prefix: prefix)) | ||||
|             { | ||||
|                 blobs.Add(new BlobFileAdapter | ||||
|                 { | ||||
|                     Name = blob.Name, | ||||
|                     Uri = $"{_blobContainerClient.Uri}/{blob.Name}", | ||||
|                     ContentType = blob.Properties.ContentType, | ||||
|                     Size = blob.Properties.ContentLength, | ||||
|                     DateUpload = blob.Properties.LastModified?.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss"), | ||||
|                     ShortDate = blob.Properties.LastModified?.UtcDateTime.ToString("MM-dd-yyyy"), | ||||
|                     Status = "Available" | ||||
|                 }); | ||||
|             } | ||||
|  | ||||
|             return blobs; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Uploads a blob to the container. | ||||
|         /// </summary> | ||||
|         public async Task<BlobFileAdapter> UploadBlobAsync(BlobAddDto newBlob) | ||||
|         { | ||||
|             var blobClient = _blobContainerClient.GetBlobClient(newBlob.FileName); | ||||
|  | ||||
|             using var stream = new MemoryStream(newBlob.FileContent); | ||||
|             await blobClient.UploadAsync(stream, overwrite: true); | ||||
|  | ||||
|             var properties = await blobClient.GetPropertiesAsync(); | ||||
|  | ||||
|             return new BlobFileAdapter | ||||
|             { | ||||
|                 Name = newBlob.FileName ?? "", | ||||
|                 Uri = blobClient.Uri.ToString(), | ||||
|                 ContentType = properties.Value.ContentType, | ||||
|                 Size = properties.Value.ContentLength, | ||||
|                 DateUpload = properties.Value.LastModified.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss"), | ||||
|                 ShortDate = properties.Value.LastModified.UtcDateTime.ToString("MM-dd-yyyy"), | ||||
|                 Status = "Uploaded" | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Deletes a blob from the container. | ||||
|         /// </summary> | ||||
|         public async Task<BlobFileAdapter?> DeleteBlobsAsync(string fileName) | ||||
|         { | ||||
|             var blobClient = _blobContainerClient.GetBlobClient(fileName); | ||||
|  | ||||
|             if (await blobClient.ExistsAsync()) | ||||
|             { | ||||
|                 var properties = await blobClient.GetPropertiesAsync(); | ||||
|                 var _response = await blobClient.DeleteIfExistsAsync(); | ||||
|  | ||||
|                 return new BlobFileAdapter | ||||
|                 { | ||||
|                     Name = fileName, | ||||
|                     Uri = blobClient.Uri.ToString(), | ||||
|                     ContentType = properties.Value.ContentType, | ||||
|                     Size = properties.Value.ContentLength, | ||||
|                     DateUpload = properties.Value.LastModified.UtcDateTime.ToString("yyyy-MM-dd HH:mm:ss"), | ||||
|                     ShortDate = properties.Value.LastModified.UtcDateTime.ToString("MM-dd-yyyy"), | ||||
|                     Status = _response ? "Deleted" : "Failed to delete" | ||||
|                 }; | ||||
|             } | ||||
|  | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Downloads a blob's content. | ||||
|         /// </summary> | ||||
|         public async Task<BlobDownloadInfo> DownloadBlobsAsync(string blobName) | ||||
|         { | ||||
|             var blobClient = _blobContainerClient.GetBlobClient(blobName); | ||||
|  | ||||
|             if (!await blobClient.ExistsAsync()) | ||||
|             { | ||||
|                 throw new FileNotFoundException($"Blob '{blobName}' does not exist in the container '{_containerName}'."); | ||||
|             } | ||||
|  | ||||
|             return await blobClient.DownloadAsync(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Generates a secure download URI for a specified blob in the storage container. | ||||
|         /// </summary> | ||||
|         /// <param name="blobName">The name of the blob for which the download URI is being generated.</param> | ||||
|         /// <returns> | ||||
|         /// An instance of <see cref="BlobDownloadUriAdapter"/> containing the generated URI, blob name, and status. | ||||
|         /// </returns> | ||||
|         /// <remarks> | ||||
|         /// The generated URI includes a Shared Access Signature (SAS) token, which allows secure, time-limited access to the blob. | ||||
|         /// The SAS token grants read-only access to the blob for a duration of 5 minutes starting from the current time. | ||||
|         /// </remarks> | ||||
|         /// <exception cref="ArgumentNullException">Thrown if <paramref name="blobName"/> is null or empty.</exception> | ||||
|         /// <exception cref="StorageException">Thrown if there is an issue communicating with the Azure Blob service.</exception> | ||||
|         public BlobDownloadUriAdapter GenerateBlobDownloadUri(string blobName) | ||||
|         { | ||||
|             var delegationKey = _blobServiceClient.GetUserDelegationKey(DateTimeOffset.UtcNow, | ||||
|                                                                     DateTimeOffset.UtcNow.AddHours(2)); | ||||
|  | ||||
|             var blob = _blobContainerClient.GetBlobClient(blobName); | ||||
|  | ||||
|             var sasBuilder = new BlobSasBuilder() | ||||
|             { | ||||
|                 BlobContainerName = blob.BlobContainerName, | ||||
|                 BlobName = blob.Name, | ||||
|                 Resource = "b", | ||||
|                 StartsOn = DateTimeOffset.UtcNow, | ||||
|                 ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(5), | ||||
|             }; | ||||
|             sasBuilder.SetPermissions(BlobAccountSasPermissions.Read); | ||||
|             sasBuilder.Protocol = SasProtocol.Https; | ||||
|  | ||||
|             var blobUriBuilder = new BlobUriBuilder(blob.Uri) | ||||
|             { | ||||
|                 Sas = sasBuilder.ToSasQueryParameters(delegationKey, _blobServiceClient.AccountName) | ||||
|             }; | ||||
|  | ||||
|             return new BlobDownloadUriAdapter | ||||
|             { | ||||
|                 Uri = blobUriBuilder.ToUri(), | ||||
|                 Name = blob.Name, | ||||
|                 Status = "Available" | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Retrieves the hierarchical folder structure. | ||||
|         /// </summary> | ||||
|         public async Task<List<BlobStorageFolder>> GetFolderHierarchyAsync(string prefix) | ||||
|         { | ||||
|             var rootFolder = new BlobStorageFolder { Name = prefix }; | ||||
|             await PopulateFolderAsync(rootFolder, prefix); | ||||
|             return new List<BlobStorageFolder> { rootFolder }; | ||||
|         } | ||||
|  | ||||
|         private async Task PopulateFolderAsync(BlobStorageFolder folder, string? prefix) | ||||
|         { | ||||
|             await foreach (var blobHierarchy in _blobContainerClient.GetBlobsByHierarchyAsync(prefix: prefix, delimiter: "/")) | ||||
|             { | ||||
|                 if (blobHierarchy.IsPrefix) | ||||
|                 { | ||||
|                     var subFolder = new BlobStorageFolder { Name = blobHierarchy.Prefix.TrimEnd('/') }; | ||||
|                     folder.SubFolders.Add(subFolder); | ||||
|                     await PopulateFolderAsync(subFolder, blobHierarchy.Prefix); | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     folder.Files.Add(new BlobStorageFilesAdapter(Content: blobHierarchy.Prefix,  //Fix | ||||
|                                                                 Name: blobHierarchy.Blob.Name, | ||||
|                                                                 ContentType: "", | ||||
|                                                                 DownloadUrl: $"{_blobContainerClient.Uri}/{blobHierarchy.Blob.Name}")); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         public async Task<Dictionary<string, List<string>>> ListNeighborFoldersAsync(string? prefix) | ||||
|         { | ||||
|             await ListFoldersInTrieAsync(prefix); | ||||
|  | ||||
|             var groupedFolders = _trie.SearchByPrefix(prefix) | ||||
|                 .OrderBy(folder => folder) | ||||
|                 .GroupBy(folder => folder.Substring(0, 1)) | ||||
|                 .ToDictionary(group => group.Key, group => group.ToList()); | ||||
|  | ||||
|             return groupedFolders; | ||||
|         } | ||||
|         private async Task ListFoldersInTrieAsync(string? prefix) | ||||
|         { | ||||
|             await foreach (var blobHierarchy in _blobContainerClient.GetBlobsByHierarchyAsync(prefix: prefix, delimiter: "/")) | ||||
|             { | ||||
|                 if (blobHierarchy.IsPrefix) | ||||
|                 { | ||||
|                     var folderName = blobHierarchy.Prefix.TrimEnd('/').Split('/').Last(); | ||||
|                     _trie.Insert(folderName); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Sergio Matias Urquin
					Sergio Matias Urquin