Es gibt immer mal wieder Szenarien, dass man eine ZIP-Dateien erstellen oder entpacken muss. Hierfür stellt .NET einem sehr hilfreiche Methoden zur Verfügung. In diesem Beitrag möchte ich nun gemeinsam mit euch eine kleine .NET Applikation entwickeln, welche einen gesamten Ordner oder eine einzelne Datei in eine ZIP-Datei überführen kann und die in der Lage ist auch ZIP-Dateien wieder zu entpacken.
Wir öffnen Visual Studio und legen ein neues .NET9 Projekt an. Anschließend fügen wir das NuGet-Paket Spectre.Console dem Projekt hinzu, da uns diese Klasse bei der Ein- und Ausgabe auf der Konsole helfen wird.
Wir erstellen einen Ordner Helpers und darin die Klasse ConsoleHelper.
internal static class ConsoleHelper
{
/// <summary>
/// Clears the console and creates the header for the application.
/// </summary>
public static void ShowHeader()
{
AnsiConsole.Clear();
Grid grid = new();
grid.AddColumn();
grid.AddRow(new FigletText("ZIP Archives").Centered().Color(Color.Red));
grid.AddRow(Align.Center(new Panel("[red]Sample by Thomas Sebastian Jensen ([link]https://www.tsjdev-apps.de[/])[/]")));
AnsiConsole.Write(grid);
AnsiConsole.WriteLine();
}
/// <summary>
/// Displays a prompt with the provided options and returns the selected option.
/// </summary>
/// <param name="options">The list of options to choose from.</param>
/// <returns>The selected option.</returns>
public static string SelectFromOptions(List<string> options)
{
ShowHeader();
return AnsiConsole.Prompt(
new SelectionPrompt<string>()
.Title("Select from the following [yellow]options[/]?")
.AddChoices(options));
}
/// <summary>
/// Displays a prompt with the provided message and returns the user input.
/// </summary>
/// <param name="prompt">The prompt message.</param>
/// <returns>The user input.</returns>
public static string GetString(string prompt)
{
ShowHeader();
return AnsiConsole.Prompt(
new TextPrompt<string>(prompt)
.PromptStyle("white")
.ValidationErrorMessage("[red]Invalid prompt[/]")
.Validate(prompt =>
{
if (prompt.Length < 3)
{
return ValidationResult.Error("[red]Value too short[/]");
}
if (prompt.Length > 200)
{
return ValidationResult.Error("[red]Value too long[/]");
}
return ValidationResult.Success();
}));
}
public static bool Confirm(string prompt)
{
return AnsiConsole.Prompt(
new TextPrompt<bool>(prompt)
.AddChoice(true)
.AddChoice(false)
.DefaultValue(true)
.WithConverter(choice => choice ? "y" : "n"));
}
/// <summary>
/// Displays an error message to the console.
/// </summary>
/// <param name="message">The error message to display.</param>
public static void DisplayError(string message)
=> WriteToConsole($"[red]{message}[/]");
/// <summary>
/// Displays a success message to the console.
/// </summary>
/// <param name="message">The success message to display.</param>
public static void DisplaySuccess(string message)
=> WriteToConsole($"[green]{message}[/]");
/// <summary>
/// Writes the specified text to the console.
/// </summary>
/// <param name="text">The text to write.</param>
private static void WriteToConsole(string text)
{
AnsiConsole.Markup(text);
AnsiConsole.WriteLine();
}
}
Die Methode ShowHeader
löscht die Konsole und zeigt eine Kopfzeile mit dem Namen der Anwendung und einer Verlinkung an. Die Methode SelectFromOptions
zeigt dem Benutzer eine Liste von Optionen zur Auswahl an. Die Methode GetString
zeigt eine Eingabeaufforderung und überprüft die Eingabe. Die Methode Confirm
bietet eine einfache Ja/Nein-Auswahl. Die beiden Methoden DisplayError
und DisplaySuccess
zeigen dem Benutzer eine Formatierung für Fehlermeldungen und Erfolgsnachrichten an.
In dem Ordner erstellen wir noch eine zweite Klasse mit dem Namen ZipArchiveHelper. Diese Klasse stellt uns drei Methoden zur Verfügung ZipDirectory, ZipFile und UnzipFile.
internal static class ZipArchiveHelper
{
/// <summary>
/// Zips the specified directory.
/// </summary>
/// <param name="path">The path of the directory to zip.</param>
public static void ZipDirectory(string path)
{
path = CleanPath(path);
if (!Directory.Exists(path))
{
ConsoleHelper.DisplayError($"The specified directory '{path}' does not exist.");
return;
}
string zipFilePath = $"{path}.zip";
DeleteIfExists(zipFilePath);
System.IO.Compression.ZipFile.CreateFromDirectory(path, zipFilePath);
ConsoleHelper.DisplaySuccess($"Directory '{path}' zipped successfully to '{zipFilePath}'");
}
/// <summary>
/// Zips the specified file.
/// </summary>
/// <param name="filePath">The path of the file to zip.</param>
public static void ZipFile(string filePath)
{
filePath = CleanPath(filePath);
if (!File.Exists(filePath))
{
ConsoleHelper.DisplayError($"The specified file '{filePath}' does not exist.");
return;
}
string zipFilePath = $"{filePath}.zip";
DeleteIfExists(zipFilePath);
using FileStream zipToOpen = new(zipFilePath, FileMode.Create);
using ZipArchive archive = new(zipToOpen, ZipArchiveMode.Update);
archive.CreateEntryFromFile(filePath, Path.GetFileName(filePath));
ConsoleHelper.DisplaySuccess($"File '{filePath}' zipped successfully to '{zipFilePath}'");
}
/// <summary>
/// Unzips the specified file.
/// </summary>
/// <param name="path">The path of the file to unzip.</param>
public static void UnzipFile(string path)
{
path = CleanPath(path);
if (!File.Exists(path))
{
ConsoleHelper.DisplayError($"The specified file '{path}' does not exist.");
return;
}
string extractPath = $"{path}-unzip";
System.IO.Compression.ZipFile.ExtractToDirectory(path, extractPath, overwriteFiles: true);
ConsoleHelper.DisplaySuccess($"File '{path}' unzipped successfully to '{extractPath}'");
}
/// <summary>
/// Cleans the specified path by trimming leading and trailing slashes.
/// </summary>
/// <param name="path">The path to clean.</param>
/// <returns>The cleaned path.</returns>
private static string CleanPath(string path)
=> path.Trim('/').Trim('\\');
/// <summary>
/// Deletes the specified file if it exists.
/// </summary>
/// <param name="filePath">The path of the file to delete.</param>
private static void DeleteIfExists(string filePath)
{
if (File.Exists(filePath))
{
File.Delete(filePath);
}
}
}
Die Methode ZipDirectory
erstellt ein ZIP-Archiv für ein ganzes Verzeichnis. Mit der Methode ZipFile
wird eine einzelne Datei in ein ZIP-Archiv umgewandelt. Die UnzipFile
-Methode entpackt ein ZIP-Archiv. Zusätzlich zu den Hauptmethoden enthält die Klasse zwei nützliche Hilfsfunktionen: DeleteIfExists
– Löscht eine Datei, falls diese bereits existiert, um Konflikte zu vermeiden. CleanPath
– Entfernt führende und nachfolgende Schrägstriche (/
und \
) aus einem Pfad.
Um das ganze nun testen zu können, erstellen wir eine Klasse Statics. Diese Klasse beinhaltet alle drei Optionen, die wir in unseren Konsolen-Anwendung durchführen wollen.
internal static class Statics
{
/// <summary>
/// The key to zip a directory.
/// </summary>
public const string ZipDirectory = "Zip Directory";
/// <summary>
/// The key to zip a file.
/// </summary>
public const string ZipFile = "Zip File";
/// <summary>
/// The key to unzip a file.
/// </summary>
public const string UnzipFile = "Unzip File";
}
Nun öffnen wir die Datei Program.cs und passen diese entsprechend an.
bool shouldRun = true;
// Display the header
ConsoleHelper.ShowHeader();
while (shouldRun)
{
// Select the operation to perform.
var mode =
ConsoleHelper.SelectFromOptions(
[Statics.ZipDirectory, Statics.ZipFile, Statics.UnzipFile]);
switch (mode)
{
// Zip a directory
case Statics.ZipDirectory:
string directoryPath =
ConsoleHelper.GetString(
"Enter the path to the directory to zip:");
ZipArchiveHelper.ZipDirectory(directoryPath);
break;
// Zip a file
case Statics.ZipFile:
string filePath =
ConsoleHelper.GetString(
"Enter the path to the file to zip:");
ZipArchiveHelper.ZipFile(filePath);
break;
// Unzip a file
case Statics.UnzipFile:
string zipFilePath =
ConsoleHelper.GetString(
"Enter the path to the zip file to unzip:");
ZipArchiveHelper.UnzipFile(zipFilePath);
break;
}
// Ask the user if they want to continue
shouldRun =
ConsoleHelper.Confirm("Do you want to continue?");
}
Dieser Code demonstriert ein einfaches Konsolenprogramm in C#, das es dem Benutzer ermöglicht, Dateien und Verzeichnisse zu komprimieren und Archive zu entpacken. Mit der ConsoleHelper
– und ZipArchiveHelper
-Klasse wird eine übersichtliche, benutzerfreundliche Oberfläche geschaffen. Durch den Einsatz von Spectre.Console
wirkt die Konsole ansprechend und interaktiv.
Das Programm startet mit dem Anzeigen eines Headers über ConsoleHelper.ShowHeader()
, um den Benutzer auf das Programm einzustimmen. Das Programm läuft in einer while
-Loop, solange der Benutzer die Anwendung nicht beenden möchte. Die Loop erlaubt es, mehrere Komprimierungs- oder Entpackungsoperationen in einer Sitzung durchzuführen. Über ConsoleHelper.SelectFromOptions
kann der Benutzer eine der drei Hauptfunktionen auswählen. Die Auswahl wird mithilfe eines switch
-Statements verarbeitet. Nach jeder Operation wird der Benutzer gefragt, ob er eine weitere Aktion durchführen möchte. Wenn die Antwort false
ist, beendet sich das Programm.