Zum Fußzeileninhalt springen
Iron Academy Logo
Lernen Sie C#
Lernen Sie C#

Andere Kategorien

Asynchrones Zippen in .NET 10: Erstellen oder Extrahieren in einer Zeile

[[academy-video-youtube({"vid": "YgQ3ta6455A", "start_time": "0", "title": "Async Zip in .NET 10: One Line Create or Extract", "creator": "Tim Corey", "length": "~12m"})]]

Zip-Datei-Operationen in C# waren schon immer über System.IO.Compression möglich, aber jeder Aufruf war synchron, was bedeutet, dass der Thread blockierte, bis das Archiv vollständig geschrieben oder gelesen war. .NET 10 ändert dies mit einer Reihe von asynchronen Überladungen, die es Ihnen ermöglichen, Zip-Dateien zu erstellen, zu extrahieren und zu füllen, ohne den aufrufenden Thread zu blockieren.

Diese Anleitung demonstriert die neuen asynchronen Zip-Überladungen in .NET 10, basierend auf Tim Coreys aktuellem Leitfaden. Wir betrachten drei zunehmend detaillierte Ansätze: einen Einzeiler zum Erstellen eines Archivs, einen einzelnen Aufruf zum Entpacken und eine manuelle Methode für volle Kontrolle, während wir auch eine scoped using-Fallstrick untersuchen.

Einrichtung: Pfade und der Quellordner

[0:28 - 1:55] Die Einrichtung beginnt mit einer Konsolenanwendung, die auf .NET 10 abzielt und eine einzelne Using-Direktive verwendet:

using System.IO.Compression;
using System.IO.Compression;

Drei Zeichenfolgenvariablen definieren die im gesamten Demo verwendeten Pfade:

string sourceDirectory    = @"C:\temp\test";
string destinationZipFile = @"C:\temp\archive.zip";
string destinationDirectory = @"C:\temp\extracted";
string sourceDirectory    = @"C:\temp\test";
string destinationZipFile = @"C:\temp\archive.zip";
string destinationDirectory = @"C:\temp\extracted";

Das wörtliche String-Präfix (@) vermeidet die Notwendigkeit, Rückwärtsschrägstriche zu verdoppeln. sourceDirectory ist der Ordner zum Zusammenpacken. destinationZipFile ist der vollständige Pfad für das zu erstellende Archiv. destinationDirectory ist der Ort, an dem der Inhalt nach dem Extrahieren landet.

Ein praktischer Hinweis: Richten Sie destinationZipFile niemals auf einen Pfad innerhalb von sourceDirectory. Das Schreiben des Zip in den Ordner, der gezippt wird, verursacht eine rekursive Leseschleife, die den Vorgang unterbricht.

Der Testordner enthält zwei Dateien im Stammverzeichnis und eine dritte Datei in einem Unterverzeichnis, was wichtig ist, wenn man die includeBaseDirectory-Option und die relative Pfadbehandlung im manuellen Ansatz betrachtet.

Erstellen eines Archivs in einer Zeile

[2:35 - 4:20] Der asynchrone Erstellungsaufruf ist ein direkter Ersatz für die synchrone ZipFile.CreateFromDirectory:

await ZipFile.CreateFromDirectoryAsync(
    sourceDirectory,
    destinationZipFile,
    CompressionLevel.SmallestSize,
    includeBaseDirectory: false);
await ZipFile.CreateFromDirectoryAsync(
    sourceDirectory,
    destinationZipFile,
    CompressionLevel.SmallestSize,
    includeBaseDirectory: false);

CompressionLevel.SmallestSize priorisiert die kleinste Ausgabe auf Kosten etwas zusätzlicher Verarbeitungszeit. CompressionLevel.Fastest macht das Gegenteil. Für die meisten Entwicklungsszenarien ist der Unterschied vernachlässigbar, aber auf einem Webserver, der viele Archive parallel verarbeitet, lohnt sich der Abwägung.

includeBaseDirectory steuert, ob der Name des Quellverzeichnisses als Stammeintrag im Archiv erscheint. Mit false (Standard) öffnet sich das Zip direkt zu den Dateien. Die Übergabe von true statt dessen platziert einen test-Ordner im Stamm, mit den eigentlichen Dateien darin. Die meisten Anwendungsfälle profitieren davon, dies auf false zu setzen.

Extrahieren eines Archivs in einer Zeile

[4:45 - 5:55] Die Extraktion folgt dem gleichen Muster:

await ZipFile.ExtractToDirectoryAsync(
    destinationZipFile,
    destinationDirectory,
    overwriteFiles: false);
await ZipFile.ExtractToDirectoryAsync(
    destinationZipFile,
    destinationDirectory,
    overwriteFiles: false);

destinationDirectory wird automatisch erstellt, wenn es noch nicht existiert. Der overwriteFiles-Parameter hat standardmäßig false, was eine Ausnahme auslöst, wenn eine Datei im Archiv bereits am Zielpfad existiert. Setzen Sie ihn auf true, um vorhandene Dateien leise zu ersetzen. Das zweimalige Ausführen der Extraktion zeigt dies: Der erste Durchlauf gelingt und erstellt den Ordner, während der zweite eine Ausnahme auslöst, es sei denn, overwriteFiles: true ist angegeben.

Selektives Zippen: Dateien einzeln hinzufügen

[6:15 - 9:50] Der Einzeilenansatz zippt ein ganzes Verzeichnis ohne Filterung. Wenn Sie nur bestimmte Dateien einbeziehen müssen, erstellen Sie das Archiv manuell mit einem FileStream und ZipArchive:

await using FileStream zipStream = new FileStream(
    destinationZipFile,
    FileMode.Create,
    FileAccess.Write,
    FileShare.None,
    bufferSize: 4096,
    useAsync: true);

using ZipArchive archive = await ZipArchive.CreateAsync(
    zipStream,
    ZipArchiveMode.Create,
    leaveOpen: false,
    entryNameEncoding: null);
await using FileStream zipStream = new FileStream(
    destinationZipFile,
    FileMode.Create,
    FileAccess.Write,
    FileShare.None,
    bufferSize: 4096,
    useAsync: true);

using ZipArchive archive = await ZipArchive.CreateAsync(
    zipStream,
    ZipArchiveMode.Create,
    leaveOpen: false,
    entryNameEncoding: null);

Einige Parameter hier sind es wert, verstanden zu werden. FileMode.Create überschreibt jede vorhandene Datei an diesem Pfad. FileMode.CreateNew löst stattdessen eine Ausnahme aus, wenn die Datei bereits existiert. FileShare.None sperrt die Datei exklusiv, während das Archiv geschrieben wird, und verhindert, dass andere Prozesse sie während der Operation lesen oder schreiben.

useAsync: true auf dem FileStream ermöglicht asynchrone Ein-/Ausgabe auf Betriebssystemebene. Beachten Sie, dass das Einstellen dieser Option ohne tatsächliche Nutzung von asynchronen Aufrufen weiter unten den Prozess erheblich verlangsamen kann, manchmal um das Zehnfache. Da die Eintrag-Schleife await verwendet, ist useAsync: true hier die richtige Wahl. Bei einem synchronen Code-Pfad belassen Sie es auf dem Standardwert false.

leaveOpen: false auf dem ZipArchive weist es an, den zugrunde liegenden Stream zu schließen und zu leeren, wenn es entsorgt wird. entryNameEncoding: null belässt die Standardkodierung, die laut Dokumentation belassen werden sollte, es sei denn, es gibt einen speziellen Grund, sie zu ändern.

Mit dem archivfertigen Inhalt, rufen Sie die Quellinhalte ab und schreiben Sie jeden Eintrag:

string[] files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories);

foreach (string filePath in files)
{
    string relativePathAndName = Path.GetRelativePath(sourceDirectory, filePath);
    await archive.CreateEntryFromFileAsync(filePath, relativePathAndName);
}
string[] files = Directory.GetFiles(sourceDirectory, "*", SearchOption.AllDirectories);

foreach (string filePath in files)
{
    string relativePathAndName = Path.GetRelativePath(sourceDirectory, filePath);
    await archive.CreateEntryFromFileAsync(filePath, relativePathAndName);
}

Directory.GetFiles mit SearchOption.AllDirectories ruft Dateien aus verschachtelten Unterordnern ab, wodurch die Unterordnerstruktur im Archiv beibehalten wird. Das Muster-Argument ("*") ist, wo Sie nach Erweiterung filtern: "*.txt" würde das Archiv beispielsweise auf Textdateien beschränken.

Path.GetRelativePath entfernt das absolute Präfix von jedem Dateipfad und lässt nur den Teil relativ zu sourceDirectory. Dies ist das, was als Eintragsname im Zip gespeichert wird, was die Unterordnerhierarchie originalgetreu reproduziert. Wenn Sie stattdessen Path.GetFileName(filePath) übergeben, landet jeder Eintrag unabhängig von seiner ursprünglichen Position im Zip-Stamm. Das erzeugt ein flaches Archiv, birgt jedoch das Risiko einer Namenskollision, wenn zwei Einträge denselben Dateinamen in verschiedenen Unterordnern teilen.

Die Eingeschlossene Using-Falle

[9:50 - 11:30] Wenn Sie versuchen, einen Extraktionsaufruf nach dem manuellen Zip-Block hinzuzufügen, stoßen Sie auf eine Datei-Sperr-Ausnahme: The process cannot access the file because it is being used by another process. Dies passiert, weil die using-Anweisung auf zipStream die dateibezogene Syntax verwendet, was bedeutet, dass der Stream bis zum Ende der Datei offen bleibt, anstatt am Ende des Zip-Blocks geschlossen zu werden. Der Extraktionsversuch läuft, während die Sperre noch gehalten wird.

Die Konvertierung in eine geklammerte Form löst dieses Problem:

// Before (file-scoped: stream stays open until end of file)
await using FileStream zipStream = new FileStream(...);

// After (block-scoped; stream released at the closing brace)
await using (FileStream zipStream = new FileStream(...))
{
    // zip operations here
}
// stream is now closed; extraction can proceed safely
// Before (file-scoped: stream stays open until end of file)
await using FileStream zipStream = new FileStream(...);

// After (block-scoped; stream released at the closing brace)
await using (FileStream zipStream = new FileStream(...))
{
    // zip operations here
}
// stream is now closed; extraction can proceed safely

Das Einfügen der Zip-Arbeit in Klammern und das Entfernen des nachgestellten Semikolons begrenzt die Lebensdauer des Streams auf diesen expliziten Geltungsbereich. Sobald die Ausführung die schließende Klammer verlässt, wird die Sperre freigegeben und ein nachfolgender Extraktionsaufruf kann dieselbe Datei ohne Konflikte öffnen.

Abschluss

[11:40 - Ende] Die Einzeiler behandeln die häufigen Fälle: CreateFromDirectoryAsync für das Archivieren eines ganzen Ordners und ExtractToDirectoryAsync für das Entpacken. Wenn Sie die Kontrolle darüber benötigen, welche Dateien in das Archiv gelangen, geben Ihnen der manuelle FileStream und ZipArchive Ansatz Filterung, Umbenennung und Pfad-Umschreibung auf der Eintragsebene.

Zusammenfassend: fügen Sie using System.IO.Compression hinzu, rufen Sie await ZipFile.CreateFromDirectoryAsync oder await ZipFile.ExtractToDirectoryAsync für unkomplizierte Fälle auf und greifen Sie für das manuelle ZipArchive-Pfad, wenn Sie Einträge filtern oder umbenennen müssen. Scope Ihre using-Blöcke mit geschweiften Klammern, wenn der Stream freigegeben werden muss, bevor der nachfolgende Code ausgeführt wird. Diese Ergänzungen machen asynchrone Muster im gesamten Zip-Workflow in .NET 10 verfügbar.

Sehen Sie sich das vollständige Video auf Tim Coreys YouTube Kanal an, um den Live-Coding-Nachahmer zu folgen.

Hero Worlddot related to Asynchrones Zippen in .NET 10: Erstellen oder Extrahieren in einer Zeile
Hero Affiliate related to Asynchrones Zippen in .NET 10: Erstellen oder Extrahieren in einer Zeile

Verdienen Sie mehr, indem Sie teilen, was Sie lieben

Erstellen Sie Inhalte für Entwickler, die mit .NET, C#, Java, Python oder Node.js arbeiten? Verwandeln Sie Ihr Fachwissen in ein zusätzliches Einkommen!

Iron-Support-Team

Wir sind 24 Stunden am Tag, 5 Tage die Woche online.
Chat
E-Mail
Rufen Sie mich an