using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using System.Text.RegularExpressions; using System.Threading.Tasks; using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; using Azure.Storage.Blobs.Specialized; using gehGassi.Core.Interfaces; using gehGassi.Core.Services; namespace gehGassi.External.Services { /// /// Service der die Verwaltung von Dateien mittels Azure ermöglicht. /// Verwendet das neue Azure-Interface /// public class AzureBlobFileService : IFileService { internal BlobServiceClient BlobServiceClient; /// /// Erstellt eine Instanz /// /// Connectionstring für Azure-Storage public AzureBlobFileService(string connectionString) { BlobServiceClient = new BlobServiceClient(connectionString); InitContainers(); } /// /// Initialisieren der Container /// /// Task private void InitContainers() { var container = BlobServiceClient.GetBlobContainerClient(FileServiceHelper.DocumentContainer); container.CreateIfNotExists(); container = BlobServiceClient.GetBlobContainerClient(FileServiceHelper.TempContainer); container.CreateIfNotExists(); } /// /// Speichern einer Datei in einen Container /// /// Name des Containers /// Dateiname (inkl. Pfad, wenn gewünscht) /// Datei als Byte-Array /// Überschreiben vorhandener Daten /// Absoluter Dateiname public async Task StoreAsync(string containerName, string filename, byte[] file, bool overwrite = true) { var container = BlobServiceClient.GetBlobContainerClient(containerName); var blob = container.GetBlockBlobClient(filename); await using var stream = new MemoryStream(file); await blob.UploadAsync(stream); return blob.Name; } /// /// Speichern einer Datei in einen Container /// /// Name des Containers /// Dateiname (inkl. Pfad, wenn gewünscht) /// Datei als Stream /// Überschreiben vorhandener Daten /// Absoluter Dateiname public async Task StoreAsync(string containerName, string filename, Stream fileStream, bool overwrite = true) { var container = BlobServiceClient.GetBlobContainerClient(containerName); var blob = container.GetBlockBlobClient(filename); await blob.UploadAsync(fileStream); return blob.Name; } /// /// Umbenennen einer Datei in einem Container /// /// Name des Containers /// Dateiname (inkl. Pfad, wenn gewünscht) /// Neuer Dateiname public async Task RenameAsync(string containerName, string filename, string newFilename) { var container = BlobServiceClient.GetBlobContainerClient(containerName); var blob = container.GetBlockBlobClient(filename); var newBlob = container.GetBlockBlobClient(newFilename); await newBlob.StartCopyFromUriAsync(blob.Uri); await blob.DeleteIfExistsAsync(); } /// /// Gibt eine Datei zurück /// /// Name des Containers /// Dateiname (inkl. Pfad, wenn gewünscht) /// Datei als Byte-Array public async Task GetAsync(string containerName, string filename) { var container = BlobServiceClient.GetBlobContainerClient(containerName); var blob = container.GetBlockBlobClient(filename); // Save blob contents to a file. byte[] result = null; if (await blob.ExistsAsync()) { await using var memoryStream = new MemoryStream(); await blob.DownloadToAsync(memoryStream); result = memoryStream.ToArray(); } return result; } /// /// Gibt eine Datei zurück /// /// Name des Containers /// Dateiname (inkl. Pfad, wenn gewünscht) /// Datei als Stream public async Task GetAsStreamAsync(string containerName, string filename) { var container = BlobServiceClient.GetBlobContainerClient(containerName); var blob = container.GetBlockBlobClient(filename); var memoryStream = new MemoryStream(); await blob.DownloadToAsync(memoryStream); memoryStream.Position = 0; return memoryStream; } /// /// Löschen einer Datei in einem Container /// /// Name des Containers /// Dateiname (inkl. Pfad, wenn gewünscht) public async Task DeleteAsync(string containerName, string filename) { var container = BlobServiceClient.GetBlobContainerClient(containerName); var blob = container.GetBlockBlobClient(filename); await blob.DeleteIfExistsAsync(); } /// /// Gibt eine Liste aller Dateien eines Containers zurück die sich in einem Directory befinden /// /// Name des Containers /// Verzeichnis. /// Teil eines Dateinamens für die suche /// Liste von Dateien public async Task> ListFilesAsync(string containerName, string directory = "", string fileNamePart = "") { var result = new List(); Wildcard wildCard = null; if (!string.IsNullOrWhiteSpace(fileNamePart)) wildCard = new Wildcard(fileNamePart, RegexOptions.IgnoreCase); var container = BlobServiceClient.GetBlobContainerClient(containerName); await foreach (var blobPage in container.GetBlobsByHierarchyAsync(BlobTraits.None, BlobStates.None, prefix: directory).AsPages()) { // A hierarchical listing may return both virtual directories and blobs. foreach (var item in blobPage.Values) { if (item.IsPrefix) continue; if (!item.IsBlob) continue; if (wildCard != null) { if (wildCard.IsMatch(GetFilename(item.Blob.Name))) result.Add(item.Blob.Name); } else result.Add(item.Blob.Name); } } return result; } /// /// Gibt eine Liste aller Verzeichnise zurück die sich im Container befindne /// /// Name des Containers /// wenn angegeben, wird von dem Verzeihnis als root ausgegangen /// public async Task> ListDirectoriesAsync(string containerName, string root = "") { var result = new List(); var container = BlobServiceClient.GetBlobContainerClient(containerName); await foreach (var blobPage in container.GetBlobsByHierarchyAsync(delimiter: "/", prefix: root).AsPages()) { result.AddRange(from item in blobPage.Values where item.IsPrefix select item.Prefix); } return result; } /// /// Koperien von Dateinen eines Containers in einen anderen /// /// Quellcontainer /// Quellpfad /// ZielContainer /// ZielPfad public async Task CopyFilesAsync(string sourceContainer, string sourceDirectory, string targetContainer, string targetDirectory) { var cloudSourceContainer = BlobServiceClient.GetBlobContainerClient(sourceContainer); var cloudTargetContainer = BlobServiceClient.GetBlobContainerClient(targetContainer); var blobs = new List(); await foreach (var blobPage in cloudSourceContainer.GetBlobsByHierarchyAsync(BlobTraits.None, BlobStates.None, prefix: sourceDirectory).AsPages()) { blobs.AddRange(from item in blobPage.Values where item.IsBlob select item.Blob); } foreach (var blobItem in blobs) { var sourceClient = cloudSourceContainer.GetBlockBlobClient(blobItem.Name); var copyClient = cloudTargetContainer.GetBlockBlobClient(blobItem.Name.Replace(sourceDirectory, targetDirectory)); await copyClient.StartCopyFromUriAsync(sourceClient.Uri); } } /// /// Koperien von Dateien eines Pfades in einen anderen /// /// Name des Containers /// Quellpfad /// ZielPfad public async Task CopyFilesAsync(string containerName, string sourceDirectory, string targetDirectory) { await CopyFilesAsync(containerName, sourceDirectory, containerName, targetDirectory); } /// /// Verschieben von Dateien eines Containers in einen anderen /// /// Quellcontainer /// Quellpfad /// ZielContainer /// ZielPfad public async Task MoveFilesAsync(string sourceContainer, string sourceDirectory, string targetContainer, string targetDirectory) { var cloudSourceContainer = BlobServiceClient.GetBlobContainerClient(sourceContainer); var cloudTargetContainer = BlobServiceClient.GetBlobContainerClient(targetContainer); var blobs = new List(); await foreach (var blobPage in cloudSourceContainer.GetBlobsByHierarchyAsync(BlobTraits.None, BlobStates.None, prefix: sourceDirectory).AsPages()) { blobs.AddRange(from item in blobPage.Values where item.IsBlob select item.Blob); } foreach (var blobItem in blobs) { var sourceClient = cloudSourceContainer.GetBlockBlobClient(blobItem.Name); var copyClient = cloudTargetContainer.GetBlockBlobClient(blobItem.Name.Replace(sourceDirectory, targetDirectory)); await copyClient.StartCopyFromUriAsync(sourceClient.Uri); await sourceClient.DeleteAsync(); } } /// /// Verschieben von Dateien eines Pfades in einen anderen /// /// Name des Containers /// Quellpfad /// ZielPfad public async Task MoveFilesAsync(string containerName, string sourceDirectory, string targetDirectory) { await MoveFilesAsync(containerName, sourceDirectory, containerName, targetDirectory); } /// /// Löschen des Inhalts eines Verzeichnises /// /// Name des Containers /// Verzeichnis /// Optional: Dateinamen Wildcard public async Task ClearDirectoryAsync(string containerName, string directory, string fileNamePart = "") { Wildcard wildCard = null; if (!string.IsNullOrWhiteSpace(fileNamePart)) wildCard = new Wildcard(fileNamePart, RegexOptions.IgnoreCase); var container = BlobServiceClient.GetBlobContainerClient(containerName); await foreach (var blobPage in container.GetBlobsByHierarchyAsync(BlobTraits.None, BlobStates.None, prefix: directory).AsPages()) { // A hierarchical listing may return both virtual directories and blobs. foreach (var item in blobPage.Values) { if (item.IsPrefix) continue; if (!item.IsBlob) continue; if (wildCard != null) { if (wildCard.IsMatch(GetFilename(item.Blob.Name))) { var client = container.GetBlockBlobClient(item.Blob.Name); await client.DeleteAsync(); } } else { var client = container.GetBlockBlobClient(item.Blob.Name); await client.DeleteAsync(); } } } } /// /// Löscht einen Container und den Inhalt /// /// Name des Containers public async Task DeleteContainerAsync(string containerName) { var container = BlobServiceClient.GetBlobContainerClient(containerName); await container.DeleteAsync(); } /// /// Löscht Dateien aus einem Ordner auf welche /// vor dem Datum das letzte mal zugegriffen wurde /// /// Name des Containers /// Verzeichnis /// Datum public async Task DeleteOldFilesAsync(string containerName, string directory, DateTimeOffset date) { var container = BlobServiceClient.GetBlobContainerClient(containerName); await foreach (var blobPage in container.GetBlobsByHierarchyAsync(BlobTraits.None, BlobStates.None, prefix: directory).AsPages()) { foreach (var item in blobPage.Values) { if (item.IsPrefix) continue; if (!item.IsBlob) continue; var client = container.GetBlockBlobClient(item.Blob.Name); if (item.Blob.Properties.LastModified < date) await client.DeleteAsync(); } } } /// /// Gibt den reinen Dateinamen eines Files zurück (Ohne Directories usw) /// /// Dateiname /// Reiner Dateiname public string GetFlatFilename(string fileName) { return GetFilename(fileName); } #region private /// /// Gibt den Dateinamen einer Datei ohne lokale Pfadangaben zurück /// /// Pfad + Dateinamen /// Dateiname ohne Pfadangaben private string GetFilename(string path) { var fileName = path.Split(new[] { "/" }, StringSplitOptions.None).Last(); return fileName; } #endregion } /// /// Hilfsklasse für das Finden von Dateien mittels Wildcards /// [ExcludeFromCodeCoverage] internal class Wildcard : Regex { /// /// Erstellt eine instanz /// /// Wildcard-Ausdruck public Wildcard(string pattern) : base(ToRegex(pattern)) { } /// /// Erstellt eine Instanz /// /// Wildcard-Ausdruck /// Optionen public Wildcard(string pattern, RegexOptions options) : base(ToRegex(pattern), options) { } /// /// Erstellt einen regulären Ausdruck aus dem Wildcard-Ausdruck /// /// Wildcard-Ausdruck /// Wildcard-Ausdruck in Regex konvertiert public static string ToRegex(string pattern) { return "^" + Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + "$"; } } }