525 lines
20 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 gehGassi.Core.Interfaces;
namespace gehGassi.Core.Services
{
/// <summary>
/// Service der die Verwaltung von Dateien ermöglicht
/// </summary>
public class FileService : IFileService
{
private readonly string _root;
/// <summary>
/// Erstellt eine Instanz
/// </summary>
/// <param name="root">Root-Verzeichnis für alle Datei-Operationen</param>
[ExcludeFromCodeCoverage]
public FileService(string root)
{
_root = root;
if (!Directory.Exists(_root))
{
Directory.CreateDirectory(_root);
}
}
/// <summary>
/// Erstellt einen Container, wenn nicht bereits vorhanden
/// </summary>
/// <param name="name">Name des Containers</param>
private void CreateContainerIfNotExists(string name)
{
var containerRoot = Path.Combine(_root, name);
if (!Directory.Exists(containerRoot))
{
Directory.CreateDirectory(containerRoot);
}
}
/// <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>
public async Task<string> 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);
}
/// <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>
public async Task<string> 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);
}
/// <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)
{
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;
}
/// <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></returns>
public async Task<byte[]> 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;
}
/// <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></returns>
public async Task<Stream> 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;
}
/// <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)
{
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;
}
/// <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 mit Wildcard!!!</param>
/// <returns>Liste von Dateien</returns>
public async Task<List<string>> ListFilesAsync(string containerName, string directory = "", string fileNamePart = "")
{
CreateContainerIfNotExists(containerName);
var resultList = new List<string>();
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;
}
/// <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 Task<List<string>> ListDirectoriesAsync(string containerName, string root = "")
{
throw new NotImplementedException();
}
/// <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)
{
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;
}
/// <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)
{
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;
}
/// <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)
{
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;
}
/// <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)
{
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;
}
/// <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 mit *</param>
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;
}
/// <summary>
/// Löscht einen Container und den Inhalt
/// </summary>
/// <param name="containerName">Name des Containers</param>
public async Task DeleteContainerAsync(string containerName)
{
var containerRoot = Path.Combine(_root, containerName);
if (Directory.Exists(containerRoot))
Directory.Delete(containerRoot, true);
await Task.CompletedTask;
}
/// <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)
{
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;
}
/// <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)
{
var file = ToLocalFilename(fileName);
file = GetFilename(file);
return file;
}
#region private
/// <summary>
/// Konvertiert einen Dateinamen mit Pfadangaben in einen Dateinamen
/// ohne Pfade. Diese sind nun in den Dateinamen integriert
/// </summary>
/// <param name="filename">Dateiname</param>
/// <returns>Dateiname</returns>
private string ToLocalFilename(string filename)
{
filename = filename.Replace(@"[", "");
filename = filename.Replace(@"]", "");
filename = filename.Replace(@"\", "[]");
filename = filename.Replace(@"/", "[]");
return filename;
}
/// <summary>
/// Konvertiert ein Verzeichnis in die lokale Form
/// ohne Pfade. Diese sind nun in den Dateinamen integriert
/// </summary>
/// <param name="directory">Pfad</param>
/// <returns>Pfad</returns>
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;
}
/// <summary>
/// Konvertiert einen lokalen Dateinamen mit Pfadangaben
/// in einen relativen Pfad
/// </summary>
/// <param name="filename">Dateiname</param>
/// <returns>Dateiname</returns>
private string ToRelativeFilename(string filename)
{
filename = filename.Replace(@"[]", "/");
return filename;
}
/// <summary>
/// Konvertiert einen lokalen Dateinamen mit Pfadangaben
/// in einen absoluten Pfad
/// </summary>
/// <param name="filename">Dateiname</param>
/// <returns>Dateiname</returns>
private string ToAbsoluteFilename(string filename)
{
filename = filename.Replace(@"[]", @"\");
return filename;
}
/// <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("\\?", ".") + "$";
}
}
}