// Copyright © 2022 Dmitry Sikorsky. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.IO; using System.Linq; using System.Threading.Tasks; using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; using ExtCore.FileStorage.Abstractions; namespace ExtCore.FileStorage.Azure; /// /// Implements the IDirectoryProxy interface and represents a file in a Azure Storage account. /// public class FileProxy : IFileProxy { private readonly string connectionString; private readonly string filepath; private readonly string containerName; private readonly string blobName; /// /// The path of the underlying file relatively to the root one. /// public string RelativePath { get; private set; } /// /// The filename of the underlying file. /// public string Filename { get; private set; } /// /// Initializes a new instance of the FileProxy class. /// /// The Azure Storage account connection string. /// The root path of the underlying file's relative one. /// The path of the underlying file relatively to the root one. /// The filename of the underlying file. /// /// public FileProxy(string connectionString, string rootPath, string relativePath, string filename) { if (connectionString == string.Empty) throw new ArgumentException($"Value can't be empty. Parameter name: connectionString."); if (connectionString == null) throw new ArgumentNullException($"Value can't be null. Parameter name: connectionString.", default(Exception)); if (filename == string.Empty) throw new ArgumentException($"Value can't be empty. Parameter name: filename."); if (filename == null) throw new ArgumentNullException($"Value can't be null. Parameter name: filename.", default(Exception)); this.connectionString = connectionString; this.RelativePath = RelativeUrl.Combine(relativePath); this.Filename = filename; this.filepath = RelativeUrl.Combine(rootPath, this.RelativePath, this.Filename); string[] urlSegments = filepath.Split('/'); this.containerName = urlSegments.First(); this.blobName = string.Join("/", urlSegments.Skip(1)); } /// /// Checks if the underlying file exists. /// /// Returns a flag indicating if the underlying file exists. public async Task ExistsAsync() { try { BlobClient blobClient = await this.GetBlobClient(); return await blobClient.ExistsAsync(); } catch { return false; } } /// /// Reads content of the underlying file as a byte array. /// /// Content of the underlying file as a byte array. /// /// /// /// /// public async Task ReadStreamAsync() { try { BlobClient blobClient = await this.GetBlobClient(); MemoryStream stream = new MemoryStream(); await blobClient.DownloadToAsync(stream); stream.Position = 0; return stream; } catch (Exception e) { throw new FileStorageException($"Generic file storage exception: \"{this.filepath}\". See inner exception for details.", e); } } /// /// Reads content of the underlying file as a byte array. /// /// Content of the underlying file as a byte array. /// /// /// /// /// public async Task ReadBytesAsync() { try { BlobClient blobClient = await this.GetBlobClient(); BlobDownloadResult downloadResult = await blobClient.DownloadContentAsync(); return downloadResult.Content.ToArray(); } catch (Exception e) { throw new FileStorageException($"Generic file storage exception: \"{this.filepath}\". See inner exception for details.", e); } } /// /// Reads content of the underlying file as a text string. /// /// Content of the underlying file as a text string. /// /// /// /// /// public async Task ReadTextAsync() { try { BlobClient blobClient = await this.GetBlobClient(); BlobDownloadResult downloadResult = await blobClient.DownloadContentAsync(); return downloadResult.Content.ToString(); } catch (Exception e) { throw new FileStorageException($"Generic file storage exception: \"{this.filepath}\". See inner exception for details.", e); } } /// /// Writes content to the underlying file as a stream. /// /// Content to write to the underlying file as a stream. /// /// /// /// /// public async Task WriteStreamAsync(Stream inputStream) { try { BlobClient blobClient = await this.GetBlobClient(); await blobClient.UploadAsync(inputStream); } catch (Exception e) { throw new FileStorageException($"Generic file storage exception: \"{this.filepath}\". See inner exception for details.", e); } } /// /// Writes content to the underlying file as a byte array. /// /// Content to write to the underlying file as a byte array. /// /// /// /// /// public async Task WriteBytesAsync(byte[] bytes) { try { BlobClient blobClient = await this.GetBlobClient(); await blobClient.UploadAsync(BinaryData.FromBytes(bytes)); } catch (Exception e) { throw new FileStorageException($"Generic file storage exception: \"{this.filepath}\". See inner exception for details.", e); } } /// /// Writes content to the underlying file as a text string. /// /// Content to write to the underlying file as a text string. /// /// /// /// /// public async Task WriteTextAsync(string text) { try { BlobClient blobClient = await this.GetBlobClient(); await blobClient.UploadAsync(BinaryData.FromString(text)); } catch (Exception e) { throw new FileStorageException($"Generic file storage exception: \"{this.filepath}\". See inner exception for details.", e); } } /// /// Deletes the underlying file. /// /// /// /// /// /// public async Task DeleteAsync() { try { BlobClient blobClient = await this.GetBlobClient(); await blobClient.DeleteAsync(); } catch (Exception e) { throw new FileStorageException($"Generic file storage exception: \"{this.filepath}\". See inner exception for details.", e); } } private async Task GetBlobClient() { BlobServiceClient blobServiceClient = new BlobServiceClient(this.connectionString); BlobContainerClient blobContainerClient = blobServiceClient.GetBlobContainerClient(this.containerName); await blobContainerClient.CreateIfNotExistsAsync(); return blobContainerClient.GetBlobClient(this.blobName); } }