Passer au contenu du pied de page
Iron Academy Logo
Apprendre le C#
Apprendre le C#

Autres catégories

Async Zip dans .NET 10 : Créer ou extraire en une ligne

Tim Corey
~12m

Les opérations de fichiers Zip en C# ont toujours été possibles via System.IO.Compression, mais chaque appel était synchrone, ce qui signifiait que le thread était bloqué jusqu'à ce que l'archive soit entièrement écrite ou lue. .NET 10 change cela avec un ensemble de surcharges asynchrones qui vous permettent de créer, extraire et remplir des fichiers zip sans occuper le thread appelant.

Ce guide montre les nouvelles surcharges zip asynchrones dans .NET 10, basé sur le récent guide de Tim Corey. Nous examinerons trois approches de plus en plus détaillées : une ligne pour créer une archive, un appel unique pour la décompresser, et une méthode manuelle pour un contrôle total, tout en examinant également un piège de portée using.

Configuration : Chemins et dossier source

[0:28 - 1:55] La configuration commence par une application console cible .NET 10 et une seule directive using :

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

Trois variables de chaîne définissent les chemins utilisés tout au long de la démo :

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";

Le préfixe de chaîne verbatim (@) évite la nécessité de doubler les antislashs. sourceDirectory est le dossier à zipper. destinationZipFile est le chemin complet de l'archive qui sera créée. destinationDirectory est l'endroit où le contenu sera placé une fois extrait.

Un avertissement pratique : ne pointez jamais destinationZipFile vers un chemin à l'intérieur de sourceDirectory. L'écriture du zip dans le dossier en cours de zippage provoque une boucle de lecture récursive qui brise le processus.

Le dossier de test contient deux fichiers à la racine et un troisième fichier dans un sous-dossier, ce qui est important lors de l'exploration de l'option includeBaseDirectory et la gestion des chemins relatifs dans l'approche manuelle.

Créer une archive en une ligne

[2:35 - 4:20] L'appel de création asynchrone est un remplacement direct pour le synchrone ZipFile.CreateFromDirectory :

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

CompressionLevel.SmallestSize privilégie la sortie la plus petite au prix d'un peu plus de temps de traitement. CompressionLevel.Fastest fait l'inverse. Pour la plupart des scénarios de développement, la différence est négligeable, mais sur un serveur web traitant de nombreux archives simultanément, le compromis vaut la peine d'être évalué.

includeBaseDirectory contrôle si le nom du répertoire source apparaît comme entrée racine dans l'archive. Avec false (le défaut), le zip s'ouvre directement sur les fichiers. Passer true place à la place un dossier test à la racine, avec les fichiers réels à l'intérieur. La plupart des cas d'utilisation bénéficient de régler cela sur false.

Extraire une Archive en Une Ligne

[4:45 - 5:55] L'extraction suit le même modèle :

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

destinationDirectory est créé automatiquement s'il n'existe pas déjà. Le paramètre overwriteFiles par défaut est false, ce qui lance une exception si un fichier de l'archive existe déjà au chemin de destination. Réglez-le sur true pour remplacer les fichiers existants sans avertissement. L'exécution de l'extraction deux fois illustre ceci : la première passe réussit et crée le dossier, tandis que la deuxième lance une exception sauf si overwriteFiles: true est spécifié.

Zippage Sélectif : Ajouter des Fichiers Un par Un

[6:15 - 9:50] L'approche en une ligne zipe un répertoire entier sans filtrage. Lorsque vous devez inclure uniquement certains fichiers, vous construisez l'archive manuellement en utilisant un FileStream et 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);

Quelques paramètres ici valent la peine d'être compris. FileMode.Create écrase tout fichier existant à ce chemin. FileMode.CreateNew lance une exception à la place si le fichier existe déjà. FileShare.None verrouille le fichier exclusivement pendant que l'archive est en cours d'écriture, empêchant d'autres processus de le lire ou de l'écrire pendant l'opération.

useAsync: true sur le FileStream permet une E/S asynchrone au niveau du système d'exploitation. Notez que définir cela sans utiliser réellement des appels async en aval peut ralentir considérablement le processus, parfois jusqu'à dix fois. Parce que la boucle d'écriture d'entrée utilise await, useAsync: true est le bon choix ici. Sur un chemin de code synchrone, laissez-le à la valeur par défaut false.

leaveOpen: false sur le ZipArchive lui dit de fermer et vider le flux sous-jacent lorsqu'il est disposé. entryNameEncoding: null conserve l'encodage par défaut, que la documentation recommande de ne pas changer sans raison spécifique.

Avec l'archive prête, récupérez le contenu source et écrivez chaque entrée :

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 avec SearchOption.AllDirectories récupère les fichiers des sous-dossiers imbriqués, ce qui permet de préserver la structure des sous-dossiers dans l'archive. L'argument du motif ("*") est là où vous filtrez par extension : "*.txt" limiterait l'archive aux fichiers texte, par exemple.

Path.GetRelativePath retire le préfixe absolu de chaque chemin de fichier, laissant seulement la portion relative à sourceDirectory. C'est ce qui est stocké comme nom d'entrée à l'intérieur du zip, reproduisant fidèlement la hiérarchie des sous-dossiers. Si vous passez au lieu Path.GetFileName(filePath), chaque entrée est placée à la racine du zip, quelle que soit sa localisation d'origine. Cela produit une archive plate, mais risque une collision de noms si deux entrées partagent un nom de fichier à travers différents sous-dossiers.

Le Piège de l'Utilisation Délimitée

[9:50 - 11:30] Si vous essayez d'ajouter un appel d'extraction après le bloc zip manuel, vous rencontrerez une exception de verrouillage de fichier : The process cannot access the file because it is being used by another process. Cela se produit parce que la déclaration using sur zipStream utilise une syntaxe définie par un fichier, ce qui signifie que le flux reste ouvert jusqu'à la fin du fichier plutôt que de se fermer à l'accolade du bloc zip. La tentative d'extraction s'exécute alors que le verrou est toujours maintenu.

Convertir en une forme avec accolades résout cela :

// 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

Enveloppant le travail de zip dans des accolades et retirant le point-virgule de fin, on limite la durée de vie du flux à cette portée explicite. Une fois que l'exécution quitte l'accolade de fermeture, le verrou est libéré et un appel d'extraction ultérieur peut ouvrir le même fichier sans conflit.

Conclusion

[11:40 - fin] Les one-liners gèrent les cas courants : CreateFromDirectoryAsync pour archiver tout un dossier et ExtractToDirectoryAsync pour le décompresser. Lorsque vous avez besoin de contrôler quels fichiers entrent dans l'archive, l'approche manuelle FileStream et ZipArchive vous donne la possibilité de filtrer, renommer et réécrire les chemins au niveau de l'entrée.

Pour récapituler : ajoutez using System.IO.Compression, appelez await ZipFile.CreateFromDirectoryAsync ou await ZipFile.ExtractToDirectoryAsync pour des cas simples, et choisissez le chemin manuel ZipArchive lorsque vous devez filtrer ou renommer des entrées. Utilisez des accolades pour vos blocs using si le flux doit être libéré avant que le code suivant ne s'exécute. Ces ajouts rendent les modèles async/await disponibles tout au long du flux de travail zip complet dans .NET 10.

Regardez la vidéo complète sur la chaîne de Tim Corey sur YouTube pour suivre le codage en direct.

Hero Worlddot related to Async Zip dans .NET 10 : Créer ou extraire en une ligne
Hero Affiliate related to Async Zip dans .NET 10 : Créer ou extraire en une ligne

Gagnez plus en partageant ce que vous aimez

Vous créez du contenu pour les développeurs travaillant avec .NET, C#, Java, Python ou Node.js ? Transformez votre expertise en revenu supplémentaire !

Équipe de soutien Iron

Nous sommes en ligne 24 heures sur 24, 5 jours sur 7.
Chat
Email
Appelez-moi