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 gehGassi.Core.Interfaces; namespace gehGassi.Core.Services { /// /// Service der die Verwaltung von Dateien ermöglicht /// public class FileService : IFileService { private readonly string _root; /// /// Erstellt eine Instanz /// /// Root-Verzeichnis für alle Datei-Operationen [ExcludeFromCodeCoverage] public FileService(string root) { _root = root; if (!Directory.Exists(_root)) { Directory.CreateDirectory(_root); } } /// /// Erstellt einen Container, wenn nicht bereits vorhanden /// /// Name des Containers private void CreateContainerIfNotExists(string name) { var containerRoot = Path.Combine(_root, name); if (!Directory.Exists(containerRoot)) { Directory.CreateDirectory(containerRoot); } } /// /// Speichern einer Datei in einen Container /// /// Name des Containers /// Dateiname (inkl. Pfad, wenn gewünscht) /// Datei als Byte-Array /// Überschreiben vorhandener Daten public async Task StoreAsync(string containerName, string filename, byte[] file, bool overwrite = true) { CreateContainerIfNotExists(containerName); var containerRoot = Path.Combine(_root, containerName); filename = ToLocalFilename(filename); var targetFile = Path.Combine(containerRoot, filename); if (!overwrite) { if (File.Exists(targetFile)) throw new IOException("File already exists"); } await File.WriteAllBytesAsync(targetFile, file); return ToRelativeFilename(filename); } /// /// Speichern einer Datei in einen Container /// /// Name des Containers /// Dateiname (inkl. Pfad, wenn gewünscht) /// Datei als Stream /// Überschreiben vorhandener Daten public async Task StoreAsync(string containerName, string filename, Stream fileStream, bool overwrite = true) { CreateContainerIfNotExists(containerName); var containerRoot = Path.Combine(_root, containerName); filename = ToLocalFilename(filename); var targetFile = Path.Combine(containerRoot, filename); if (!overwrite) { if (File.Exists(targetFile)) throw new IOException("File already exists"); } await using (var fs = new FileStream(targetFile, FileMode.Create, FileAccess.ReadWrite)) { await fileStream.CopyToAsync(fs).ConfigureAwait(false); } return ToRelativeFilename(filename); } /// /// 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) { CreateContainerIfNotExists(containerName); var containerRoot = Path.Combine(_root, containerName); filename = ToLocalFilename(filename); newFilename = ToLocalFilename(newFilename); var sourceFileName = Path.Combine(containerRoot, filename); var targetFileName = Path.Combine(containerRoot, newFilename); File.Move(sourceFileName, targetFileName); await Task.CompletedTask; } /// /// Gibt eine Datei zurück /// /// Name des Containers /// Dateiname (inkl. Pfad, wenn gewünscht) /// public async Task GetAsync(string containerName, string filename) { CreateContainerIfNotExists(containerName); var containerRoot = Path.Combine(_root, containerName); filename = ToLocalFilename(filename); var targetFile = Path.Combine(containerRoot, filename); if (File.Exists(targetFile)) { return await File.ReadAllBytesAsync(targetFile); } return null; } /// /// Gibt eine Datei zurück /// /// Name des Containers /// Dateiname (inkl. Pfad, wenn gewünscht) /// public async Task GetAsStreamAsync(string containerName, string filename) { CreateContainerIfNotExists(containerName); var containerRoot = Path.Combine(_root, containerName); filename = ToLocalFilename(filename); var targetFile = Path.Combine(containerRoot, filename); if (File.Exists(targetFile)) { await Task.CompletedTask; return File.OpenRead(targetFile); } return null; } /// /// Löschen einer Datei in einem Container /// /// Name des Containers /// Dateiname (inkl. Pfad, wenn gewünscht) public async Task DeleteAsync(string containerName, string filename) { CreateContainerIfNotExists(containerName); var containerRoot = Path.Combine(_root, containerName); filename = ToLocalFilename(filename); var targetFile = Path.Combine(containerRoot, filename); if (File.Exists(targetFile)) File.Delete(targetFile); await Task.CompletedTask; } /// /// 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 mit Wildcard!!! /// Liste von Dateien public async Task> ListFilesAsync(string containerName, string directory = "", string fileNamePart = "") { CreateContainerIfNotExists(containerName); var resultList = new List(); var containerRoot = Path.Combine(_root, containerName); directory = ToLocalDirectory(directory); if (!string.IsNullOrWhiteSpace(fileNamePart)) directory += fileNamePart; else directory += "*"; var files = Directory.GetFiles(containerRoot, directory); foreach (var file in files) { resultList.Add(ToRelativeFilename(file.Replace(containerRoot, "").Replace(@"\", ""))); } await Task.CompletedTask; return resultList; } /// /// 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 Task> ListDirectoriesAsync(string containerName, string root = "") { throw new NotImplementedException(); } /// /// Koperien von Dateinen eines Containers in einen anderen /// /// Quellcontainer /// Quellpfad /// ZielContainer /// ZielPfad public async Task CopyFilesAsync(string sourceContainer, string sourceDirectory, string targetContainer, string targetDirectory) { CreateContainerIfNotExists(sourceContainer); CreateContainerIfNotExists(targetContainer); var source = Path.Combine(_root, sourceContainer); sourceDirectory = ToLocalDirectory(sourceDirectory); var target = Path.Combine(_root, targetContainer); targetDirectory = ToLocalDirectory(targetDirectory); var sourceFiles = Directory.GetFiles(source, sourceDirectory + "*"); foreach (var sourceFile in sourceFiles) { var fileName = GetFilename(sourceFile); var targetFile = targetDirectory + fileName; targetFile = Path.Combine(target, targetFile); File.Copy(sourceFile, targetFile, true); } await Task.CompletedTask; } /// /// Koperien von Dateien eines Pfades in einen anderen /// /// Name des Containers /// Quellpfad /// ZielPfad public async Task CopyFilesAsync(string containerName, string sourceDirectory, string targetDirectory) { CreateContainerIfNotExists(containerName); var source = Path.Combine(_root, containerName); sourceDirectory = ToLocalDirectory(sourceDirectory); var target = Path.Combine(_root, source); targetDirectory = ToLocalDirectory(targetDirectory); var sourceFiles = Directory.GetFiles(source, sourceDirectory + "*"); foreach (var sourceFile in sourceFiles) { var fileName = GetFilename(sourceFile); var targetFile = targetDirectory + fileName; targetFile = Path.Combine(target, targetFile); File.Copy(sourceFile, targetFile, true); } await Task.CompletedTask; } /// /// Verschieben von Dateien eines Containers in einen anderen /// /// Quellcontainer /// Quellpfad /// ZielContainer /// ZielPfad public async Task MoveFilesAsync(string sourceContainer, string sourceDirectory, string targetContainer, string targetDirectory) { CreateContainerIfNotExists(sourceContainer); CreateContainerIfNotExists(targetContainer); var source = Path.Combine(_root, sourceContainer); sourceDirectory = ToLocalDirectory(sourceDirectory); var target = Path.Combine(_root, targetContainer); targetDirectory = ToLocalDirectory(targetDirectory); var sourceFiles = Directory.GetFiles(source, sourceDirectory + "*"); foreach (var sourceFile in sourceFiles) { var fileName = GetFilename(sourceFile); var targetFile = targetDirectory + fileName; targetFile = Path.Combine(target, targetFile); if (File.Exists(targetFile)) File.Delete(targetFile); File.Move(sourceFile, targetFile); } await Task.CompletedTask; } /// /// Verschieben von Dateien eines Pfades in einen anderen /// /// Name des Containers /// Quellpfad /// ZielPfad public async Task MoveFilesAsync(string containerName, string sourceDirectory, string targetDirectory) { CreateContainerIfNotExists(containerName); var source = Path.Combine(_root, containerName); sourceDirectory = ToLocalDirectory(sourceDirectory); var target = Path.Combine(_root, source); targetDirectory = ToLocalDirectory(targetDirectory); var sourceFiles = Directory.GetFiles(source, sourceDirectory + "*"); foreach (var sourceFile in sourceFiles) { var fileName = GetFilename(sourceFile); var targetFile = targetDirectory + fileName; targetFile = Path.Combine(target, targetFile); if (File.Exists(targetFile)) File.Delete(targetFile); File.Move(sourceFile, targetFile); } await Task.CompletedTask; } /// /// Löschen des Inhalts eines Verzeichnises /// /// Name des Containers /// Verzeichnis /// Optional: Dateinamen Wildcard mit * public async Task ClearDirectoryAsync(string containerName, string directory, string fileNamePart = "") { CreateContainerIfNotExists(containerName); var containerRoot = Path.Combine(_root, containerName); directory = ToLocalDirectory(directory); if (!string.IsNullOrWhiteSpace(fileNamePart)) directory += fileNamePart; else directory += "*"; var files = Directory.GetFiles(containerRoot, directory); foreach (var file in files) { File.Delete(file); } await Task.CompletedTask; } /// /// Löscht einen Container und den Inhalt /// /// Name des Containers public async Task DeleteContainerAsync(string containerName) { var containerRoot = Path.Combine(_root, containerName); if (Directory.Exists(containerRoot)) Directory.Delete(containerRoot, true); await Task.CompletedTask; } /// /// 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) { CreateContainerIfNotExists(containerName); var containerRoot = Path.Combine(_root, containerName); directory = ToLocalDirectory(directory); var files = Directory.GetFiles(containerRoot, directory + "*"); foreach (var file in files) { var info = new FileInfo(file); if (info.LastAccessTimeUtc < date) File.Delete(file); } await Task.CompletedTask; } /// /// Gibt den reinen Dateinamen eines Files zurück (Ohne Directories usw) /// /// Dateiname /// Reiner Dateiname public string GetFlatFilename(string fileName) { var file = ToLocalFilename(fileName); file = GetFilename(file); return file; } #region private /// /// Konvertiert einen Dateinamen mit Pfadangaben in einen Dateinamen /// ohne Pfade. Diese sind nun in den Dateinamen integriert /// /// Dateiname /// Dateiname private string ToLocalFilename(string filename) { filename = filename.Replace(@"[", ""); filename = filename.Replace(@"]", ""); filename = filename.Replace(@"\", "[]"); filename = filename.Replace(@"/", "[]"); return filename; } /// /// Konvertiert ein Verzeichnis in die lokale Form /// ohne Pfade. Diese sind nun in den Dateinamen integriert /// /// Pfad /// Pfad private string ToLocalDirectory(string directory) { if (string.IsNullOrWhiteSpace(directory)) return ""; directory = directory.Replace(@"[", ""); directory = directory.Replace(@"]", ""); directory = directory.Replace(@"\", "[]"); directory = directory.Replace(@"/", "[]"); if (!directory.EndsWith("[]")) directory += "[]"; return directory; } /// /// Konvertiert einen lokalen Dateinamen mit Pfadangaben /// in einen relativen Pfad /// /// Dateiname /// Dateiname private string ToRelativeFilename(string filename) { filename = filename.Replace(@"[]", "/"); return filename; } /// /// Konvertiert einen lokalen Dateinamen mit Pfadangaben /// in einen absoluten Pfad /// /// Dateiname /// Dateiname private string ToAbsoluteFilename(string filename) { filename = filename.Replace(@"[]", @"\"); return filename; } /// /// 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("\\?", ".") + "$"; } } }