.NET 10'da Asenkron Zip: Tek Satırda Oluştur veya Çıkart
C#'ta zip dosya işlemleri System.IO.Compression aracılığıyla her zaman mümkün olmuştur; ancak her çağrı eş zamanlı çalıştığından, arşiv tamamen yazılana veya okunana kadar çağıran iş parçacığı beklemek zorunda kalırdı. .NET 10, bu durumu, çağıran iş parçacığını kilitlemeden zip dosyaları oluşturmanıza, çıkartmanıza ve doldurmanıza izin veren bir dizi async aşırı yükleme sunarak bu durumu değiştiriyor.
Bu rehber, .NET 10'daki yeni async zip yüklemelerini Tim Corey'nin son kılavuzuna dayanarak göstermektedir. Arşiv oluşturmak için tek satırlık bir yapı, bunu açmak için tek çağrılık bir yol ve tam kontrol için manuel bir yöntem olmak üzere üç ilerleyici detaya bakacağız, ayrıca kapsamlı using bloğu tuzağına da göz atacağız.
Kurulum: Yollar ve Kaynak Klasörü
[0:28 - 1:55] Kurulum, tek bir using direktifi ve .NET 10 hedefleyen bir konsol uygulaması ile başlar:
using System.IO.Compression;using System.IO.Compression;Demo boyunca kullanılan yolları tanımlayan üç dize değişkeni:
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";Verbatim dize öneki (@) ters eğik çizgileri ikiye katlama gereksinimini önler. sourceDirectory sıkıştırılacak klasördür. destinationZipFile oluşturulacak arşivin tam yoludur. Ekstrakte edildiğinde içeriklerin yer alacağı yer destinationDirectory'tir.
Bir pratik uyarı: destinationZipFile'yi asla sourceDirectory içinde bir yola yönlendirmeyin. Zip'i, ziplenen klasörün içine yazmak, süreci bozan bir döngüsel okuma döngüsüne neden olur.
Test klasörü kök dizinde iki dosya ve alt klasörde bir üçüncü dosya içerir, bu da includeBaseDirectory seçeneğini ve manuel yaklaşımda göreceli yol işlemesini incelerken önemlidir.
Tek Satırda Bir Arşiv Oluşturma
[2:35 - 4:20] Async oluşturma çağrısı, senkron ZipFile.CreateFromDirectory için doğrudan bir yerine koyma işlevi görür:
await ZipFile.CreateFromDirectoryAsync(
sourceDirectory,
destinationZipFile,
CompressionLevel.SmallestSize,
includeBaseDirectory: false);await ZipFile.CreateFromDirectoryAsync(
sourceDirectory,
destinationZipFile,
CompressionLevel.SmallestSize,
includeBaseDirectory: false);CompressionLevel.SmallestSize, biraz fazladan işlem süresi karşılığında en küçük çıktıyı öncelik yapar. CompressionLevel.Fastest ise bunun tersini yapar. Çoğu geliştirme senaryosunda fark önemsizdir, ancak bir web sunucusunda birçok arşivi eşzamanlı olarak işlemek durumunda, bu değiş tokuş değerlendirilmeye değer.
includeBaseDirectory, arşivin içinde kaynak dizin adının bir kök girişi olarak görünüp görünmeyeceğini kontrol eder. false (varsayılan) ile zip doğrudan dosyalara açılır. Bunun yerine true geçişi yapıldığında kök dizinde test klasörü oluşturulur ve asıl dosyalar bunun içinde yer alır. Çoğu kullanım durumu için bunu false olarak ayarlamak fayda sağlar.
Tek Satırda Bir Arşivi Çıkartma
[4:45 - 5:55] Çıkarma aynı desenle takip eder:
await ZipFile.ExtractToDirectoryAsync(
destinationZipFile,
destinationDirectory,
overwriteFiles: false);await ZipFile.ExtractToDirectoryAsync(
destinationZipFile,
destinationDirectory,
overwriteFiles: false);destinationDirectory zaten mevcut değilse otomatik olarak oluşturulur. overwriteFiles parametresi false varsayılan olarak gelir ve eğer arşivdeki herhangi bir dosya hedef yolda zaten varsa bir istisna atar. Mevcut dosyaların sessizce değiştirilmesi için onu true olarak ayarlayın. Ekstraksiyonu iki kez çalıştırarak bunu gözlemleyebilirsiniz: ilk geçiş başarıya ulaşır ve klasörü oluşturur, ikinci ise overwriteFiles: true belirtilmezse bir istisna atar.
Seçmeli Zipleme: Dosyaları Tek Tek Ekleme
[6:15 - 9:50] Tek satırlık yaklaşım, bir filtreleme olmadan tüm dizini zipler. Sadece belirli dosyaları dahil etmeniz gerektiğinde, arşivi FileStream ve ZipArchive kullanarak manuel olarak oluşturursunuz.
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);Burada birkaç parametre anlamayı değerdir. FileMode.Create, o yolda mevcut olan herhangi bir dosyayı üzerine yazar. Dosya zaten mevcutsa FileMode.CreateNew yerine bir istisna atar. FileShare.None, arşiv yazılmakta iken dosyayı münhasır bir şekilde kilitler ve böylece diğer süreçlerin onu okuması veya yazması işlemi ortasında engellenir.
FileStream üzerindeki useAsync: true işletim sistemi seviyesinde asenkron I/O'yu etkinleştirir. Bunun ayarlanmasının, aşağı akışta asenkron çağrıları kullanmadan, işlemi önemli ölçüde yavaşlatabileceğini not edin, bazen bu on kata kadar çıkabilir. Girdi yazma döngüsü await kullandığı için, burada doğru seçim useAsync: true'dir. Senkron bir kod yolunda, varsayılan false olarak bırakın.
ZipArchive üzerindeki leaveOpen: false, altta yatan akışı kapatıp flush yapmasını söyler. entryNameEncoding: null, varsayılan kodlamayı korur, ki dokümantasyon bunu değiştirmemenizi önerir, özel bir nedeniniz yoksa.
Arşiv hazır olduğunda kaynak içerikleri alın ve her girişi yazın:
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 ile SearchOption.AllDirectories, dosyaları iç içe alt klasörlerden alır, bu da arşivde alt klasör yapısının korunma şeklidir. Desen bağımsız değişkeni ("*"), uzantıya göre filtreleyebileceğiniz yerdir: Örneğin "*.txt" arşivi yalnızca metin dosyalarıyla sınırlayacaktır.
Path.GetRelativePath, her dosya yolundan mutlak önekini soyar ve sadece sourceDirectory'ye göreceli kısmı bırakır. Bu, zip içinde giriş adı olarak saklanan şeydir ve alt klasör hiyerarşisini eksiksiz olarak yeniden üretir. Bunun yerine Path.GetFileName(filePath) geçirirseniz, her giriş orijinal konumuna bakılmaksızın zip köküne düşer. Bu, düz bir arşiv üretir, ancak farklı alt klasörlerde bir dosya adını paylaşan iki giriş halinde bir ad çakışması riski taşır.
Kapsamlı Using Bloğu Tuzağı
[9:50 - 11:30] Manuel zip bloğundan sonra bir çıkartma çağrısı eklemeye çalışırsanız bir dosya kilit istisnasıyla karşılaşırsınız: The process cannot access the file because it is being used by another process. Bu, zipStream üzerindeki using deyimi dosya kapsamlı sözdizimini kullandığından, akışın zip bloğunun parantez kapanışında kapanmak yerine dosyanın sonuna kadar açık kaldığı için olur. Çıkarma girişimi hala kilit tutulurken çalıştırılır.
Süslü parantez formuna dönüştürmek bunu çözer:
// 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 safelyZip işini süslü parantezler içine almak ve sonunda noktalı virgül bırakmak, akışın ömrünü bu belirli kapsamla sınırlar. Yürütme kapanış süslü parantezini terk ettikten sonra, kilit serbest bırakılır ve sonraki bir çıkarma çağrısı aynı dosyayı çatışma olmadan açabilir.
Sonuç
[11:40 - son] Tek satırlık çözümler yaygın durumları yönetir: tüm bir klasörü arşivlemek için CreateFromDirectoryAsync ve bunu açmak için ExtractToDirectoryAsync. Hangi dosyaların arşive gireceğini kontrol etmeniz gerektiğinde, manuel FileStream ve ZipArchive yaklaşımı, giriş seviyesinde filtreleme, isim değiştirme ve yol yeniden yazma sağlar.
Özetlemek gerekirse: using System.IO.Compression ekleyin, doğrudan durumlar için await ZipFile.CreateFromDirectoryAsync veya await ZipFile.ExtractToDirectoryAsync çağırın ve girdi seviyesinde filtreleme veya yeniden isimlendirmeye ihtiyaçınız olduğunda manuel ZipArchive yoluna başvurun. Akışın sonraki kod çalıştırılmadan önce serbest bırakılması gerekiyorsa, using bloklarınızı parantezler ile ele alın. Bu eklemeler, .NET 10'da tam zip iş akışı boyunca async/await desenlerini kullanılabilir hale getirir.
Canlı kodlamayı takip etmek için Tim Corey's YouTube kanalındaki tam videoyu izleyin.

