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("\\?", ".") + "$";
}
}
}