411 lines
17 KiB
C#
411 lines
17 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// Service der die Verwaltung von Dateien mittels Azure ermöglicht.
|
|
/// Verwendet das neue Azure-Interface
|
|
/// </summary>
|
|
public class AzureBlobFileService : IFileService
|
|
{
|
|
internal BlobServiceClient BlobServiceClient;
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Instanz
|
|
/// </summary>
|
|
/// <param name="connectionString">Connectionstring für Azure-Storage</param>
|
|
public AzureBlobFileService(string connectionString)
|
|
{
|
|
BlobServiceClient = new BlobServiceClient(connectionString);
|
|
InitContainers();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialisieren der Container
|
|
/// </summary>
|
|
/// <returns>Task</returns>
|
|
private void InitContainers()
|
|
{
|
|
var container = BlobServiceClient.GetBlobContainerClient(FileServiceHelper.DocumentContainer);
|
|
container.CreateIfNotExists();
|
|
container = BlobServiceClient.GetBlobContainerClient(FileServiceHelper.TempContainer);
|
|
container.CreateIfNotExists();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Speichern einer Datei in einen Container
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
|
|
/// <param name="file">Datei als Byte-Array</param>
|
|
/// <param name="overwrite">Überschreiben vorhandener Daten</param>
|
|
/// <returns>Absoluter Dateiname</returns>
|
|
public async Task<string> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Speichern einer Datei in einen Container
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
|
|
/// <param name="fileStream">Datei als Stream</param>
|
|
/// <param name="overwrite">Überschreiben vorhandener Daten</param>
|
|
/// <returns>Absoluter Dateiname</returns>
|
|
public async Task<string> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Umbenennen einer Datei in einem Container
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
|
|
/// <param name="newFilename">Neuer Dateiname</param>
|
|
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();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Datei zurück
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
|
|
/// <returns>Datei als Byte-Array</returns>
|
|
public async Task<byte[]> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Datei zurück
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
|
|
/// <returns>Datei als Stream</returns>
|
|
public async Task<Stream> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Löschen einer Datei in einem Container
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="filename">Dateiname (inkl. Pfad, wenn gewünscht)</param>
|
|
public async Task DeleteAsync(string containerName, string filename)
|
|
{
|
|
var container = BlobServiceClient.GetBlobContainerClient(containerName);
|
|
var blob = container.GetBlockBlobClient(filename);
|
|
await blob.DeleteIfExistsAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Liste aller Dateien eines Containers zurück die sich in einem Directory befinden
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="directory">Verzeichnis.</param>
|
|
/// <param name="fileNamePart">Teil eines Dateinamens für die suche</param>
|
|
/// <returns>Liste von Dateien</returns>
|
|
public async Task<List<string>> ListFilesAsync(string containerName, string directory = "", string fileNamePart = "")
|
|
{
|
|
var result = new List<string>();
|
|
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt eine Liste aller Verzeichnise zurück die sich im Container befindne
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="root">wenn angegeben, wird von dem Verzeihnis als root ausgegangen</param>
|
|
/// <returns></returns>
|
|
public async Task<List<string>> ListDirectoriesAsync(string containerName, string root = "")
|
|
{
|
|
var result = new List<string>();
|
|
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Koperien von Dateinen eines Containers in einen anderen
|
|
/// </summary>
|
|
/// <param name="sourceContainer">Quellcontainer</param>
|
|
/// <param name="sourceDirectory">Quellpfad</param>
|
|
/// <param name="targetContainer">ZielContainer</param>
|
|
/// <param name="targetDirectory">ZielPfad</param>
|
|
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<BlobItem>();
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Koperien von Dateien eines Pfades in einen anderen
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="sourceDirectory">Quellpfad</param>
|
|
/// <param name="targetDirectory">ZielPfad</param>
|
|
public async Task CopyFilesAsync(string containerName, string sourceDirectory, string targetDirectory)
|
|
{
|
|
await CopyFilesAsync(containerName, sourceDirectory, containerName, targetDirectory);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verschieben von Dateien eines Containers in einen anderen
|
|
/// </summary>
|
|
/// <param name="sourceContainer">Quellcontainer</param>
|
|
/// <param name="sourceDirectory">Quellpfad</param>
|
|
/// <param name="targetContainer">ZielContainer</param>
|
|
/// <param name="targetDirectory">ZielPfad</param>
|
|
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<BlobItem>();
|
|
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();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verschieben von Dateien eines Pfades in einen anderen
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="sourceDirectory">Quellpfad</param>
|
|
/// <param name="targetDirectory">ZielPfad</param>
|
|
public async Task MoveFilesAsync(string containerName, string sourceDirectory, string targetDirectory)
|
|
{
|
|
await MoveFilesAsync(containerName, sourceDirectory, containerName, targetDirectory);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Löschen des Inhalts eines Verzeichnises
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="directory">Verzeichnis</param>
|
|
/// <param name="fileNamePart">Optional: Dateinamen Wildcard</param>
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Löscht einen Container und den Inhalt
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
public async Task DeleteContainerAsync(string containerName)
|
|
{
|
|
var container = BlobServiceClient.GetBlobContainerClient(containerName);
|
|
await container.DeleteAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Löscht Dateien aus einem Ordner auf welche
|
|
/// vor dem Datum das letzte mal zugegriffen wurde
|
|
/// </summary>
|
|
/// <param name="containerName">Name des Containers</param>
|
|
/// <param name="directory">Verzeichnis</param>
|
|
/// <param name="date">Datum</param>
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gibt den reinen Dateinamen eines Files zurück (Ohne Directories usw)
|
|
/// </summary>
|
|
/// <param name="fileName">Dateiname</param>
|
|
/// <returns>Reiner Dateiname</returns>
|
|
public string GetFlatFilename(string fileName)
|
|
{
|
|
return GetFilename(fileName);
|
|
}
|
|
|
|
#region private
|
|
|
|
/// <summary>
|
|
/// Gibt den Dateinamen einer Datei ohne lokale Pfadangaben zurück
|
|
/// </summary>
|
|
/// <param name="path">Pfad + Dateinamen</param>
|
|
/// <returns>Dateiname ohne Pfadangaben</returns>
|
|
private string GetFilename(string path)
|
|
{
|
|
var fileName = path.Split(new[] { "/" }, StringSplitOptions.None).Last();
|
|
return fileName;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>
|
|
/// Hilfsklasse für das Finden von Dateien mittels Wildcards
|
|
/// </summary>
|
|
[ExcludeFromCodeCoverage]
|
|
internal class Wildcard : Regex
|
|
{
|
|
/// <summary>
|
|
/// Erstellt eine instanz
|
|
/// </summary>
|
|
/// <param name="pattern">Wildcard-Ausdruck</param>
|
|
public Wildcard(string pattern) : base(ToRegex(pattern))
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erstellt eine Instanz
|
|
/// </summary>
|
|
/// <param name="pattern">Wildcard-Ausdruck</param>
|
|
/// <param name="options">Optionen</param>
|
|
public Wildcard(string pattern, RegexOptions options) : base(ToRegex(pattern), options)
|
|
{
|
|
}
|
|
|
|
/// <summary>
|
|
/// Erstellt einen regulären Ausdruck aus dem Wildcard-Ausdruck
|
|
/// </summary>
|
|
/// <param name="pattern">Wildcard-Ausdruck</param>
|
|
/// <returns>Wildcard-Ausdruck in Regex konvertiert</returns>
|
|
public static string ToRegex(string pattern)
|
|
{
|
|
return "^" + Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + "$";
|
|
}
|
|
}
|
|
}
|