Async Zip .NET 10'da: Tek Satırda Oluştur veya Çıkar
C#'de ZIP dosya işlemleri her zaman System.IO.Compression ile mümkündü, ancak her çağrı senkrondu, bu da arşiv tam olarak yazılana veya okunana kadar iş parçacığının bloke olduğu anlamına geliyordu. .NET 10, arayan iş parçacığını bloke etmeden zip dosyaları oluşturmanıza, çıkartmanıza ve doldurmanıza olanak tanıyan bir dizi asenkron aşırı yükleme ile bunu değiştirir.
Bu kapsamlı kılavuz, Tim Corey'nin son kılavuzuna dayanan .NET 10 içindeki yeni async zip aşırı yüklemelerini göstermektedir. Arşiv oluşturmak için tek satırlık bir yol, bir kez çağırarak açmak ve tam kontrol için manuel bir yöntem olmak üzere giderek daha ayrıntılı üç yaklaşımı inceleyeceğiz, aynı zamanda kapsamlı using tuzağını da inceleyeceğiz.
Kurulum: Yollar ve Kaynak Klasör
[0:28 - 1:55] Kurulum, .NET 10 ve tek bir using direktifi hedefleyen bir konsol uygulamasıyla başlar.
using System.IO.Compression;using System.IO.Compression;Üç dize değişkeni, demo boyunca kullanılan yolları tanımlar:
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";Düz metin dizesi öneki (@) ters eğik çizgi çiftleme gerekliliğini ortadan kaldırır. sourceDirectory sıkıştırılacak klasördür. destinationZipFile oluşturulacak arşivin tam yoludur. Çıkarıldığında içeriğin yerleşeceği yer destinationDirectory'dir.
Pratik bir uyarı: destinationZipFile'yi asla sourceDirectory içerisinde bir yola yönlendirmeyin. Sıkıştırma dosyasını sıkıştırılan klasörün içine yazmak, süreci kıran bir yinelenen okuma döngüsüne neden olur.
Test klasörü, kökte iki dosya ve bir alt klasör içinde üçüncü bir dosya içerir; bu, manuel yaklaşımdaki includeBaseDirectory seçeneği ve göreceli yol yönetimi keşfederken önemlidir.
Tek Satırda Arşiv Oluşturma
[2:35 - 4:20] Async oluşturma çağrısı, senkron ZipFile.CreateFromDirectory için doğrudan bir yedeklemedir:
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 pahasına en küçük çıktıya öncelik verir. CompressionLevel.Fastest ise tam tersini yapar. Çoğu geliştirme senaryosunda fark ihmal edilebilir, ancak bir web sunucusunda birçok arşivi eşzamanlı olarak işlerken, takas değerlendirmeye değerdir.
Kaynak dizin adının, arşiv içinde kök girişi olarak görünüp görünmeyeceğini includeBaseDirectory kontrol eder. false (varsayılan) ile zip, doğrudan dosyalara açılır. Bunun yerine true geçirildiğinde, kökte test adlı bir klasör yerleştirilir ve gerçek dosyalar içinde yer alır. Çoğu kullanım durumu, bunu false olarak ayarlamaktan fayda sağlar.
Tek satırda bir arşivi çıkarma
[4:45 - 5:55] Çıkartma işlemi aynı deseni izler:
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ına sahiptir, bu da arşivdeki herhangi bir dosya hedefte zaten mevcutsa bir istisna fırlatır. Mevcut dosyaları sessizce değiştirmek için true olarak ayarlayın. Çıkarmayı iki kez çalıştırmak bunu gösterir: ilk geçiş başarılı olur ve klasörü oluşturur, ikinci ise overwriteFiles: true belirtilmedikçe bir istisna fırlatır.
Seçici Zipleme: Dosyalara Tek Tek Ekleme
[6:15 - 9:50] Tek satır yaklaşımı, bir dizini filtrelemeden zipler. Yalnızca belirli dosyaları dahil etmeniz gerektiğinde, arşivi bir 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 anlamamız gereken birkaç parametre var. FileMode.Create, bu yoldaki mevcut herhangi bir dosyanın üzerine yazar. Bunun yerine, dosya zaten varsa FileMode.CreateNew fırlatır. Arşiv yazılırken diğer süreçlerin onu okumasını veya yazmasını önlemek için FileShare.None dosyayı yalnızca kilitler.
FileStream üzerinde useAsync: true, işletim sistemi seviyesinde asenkron I/O'yu etkinleştirir. Aslında aşağıda asenkron çağrılar kullanmadan bunu ayarlamanın işlemi önemli ölçüde yavaşlatabileceğini, bazen on kat bile yavaşlatabileceğini unutmayın. Giriş yazma döngüsü await kullanıyor olduğu için, burada doğru seçim useAsync: true'dir. Senkron bir kod yolunda, varsayılan false'da bırakın.
ZipArchive üzerinde leaveOpen: false, atıldığında alttaki akışı kapatması ve temizlemesi talimatını verir. entryNameEncoding: null varsayılan kodlamayı tutar ve belge, değiştirmek için özel bir nedeniniz yoksa bu şekilde bırakmanızı tavsiye eder.
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);
}Gömülü alt klasörlerden dosyaları almak için Directory.GetFiles ile SearchOption.AllDirectories, bu, alt klasör yapısının arşivde korunma şeklidir. Örüntü argümanı ("*"), uzantıya göre filtreleme yapacağınız yerdir: "*.txt", örneğin, arşivi yalnızca metin dosyalarıyla sınırlayacaktır.
Path.GetRelativePath, her dosya yolundan mutlak öneki kaldırır ve yalnızca sourceDirectory'ye göre kısmı bırakır. Bu, zip içindeki giriş adı olarak depolanan şeydir ve alt klasör hiyerarşisini sadakatle yeniden üretir. Bunun yerine Path.GetFileName(filePath) geçirirseniz, her giriş orijinal konumuna bakılmaksızın ZIP köküne yerleşir. Bu, düz bir arşiv oluşturur, ancak iki giriş farklı alt klasörlerde aynı dosya adını paylaşıyorsa ad çakışması riski taşır.
Scoped Using Tuzağı
[9:50 - 11:30] Manuel zip bloğundan sonra çıkarma çağrısı eklemeye çalışırsanız, bir dosya kilit açma hatasıyla karşılaşırsınız: The process cannot access the file because it is being used by another process. Bu, zipStream üzerindeki using statement dosya kapsamlı sözdizimini kullandığı için olur, bu da akışın ZIP bloğunun karesinde kapanmak yerine dosyanın sonuna kadar açık kaldığı anlamına gelir. Çıkartma denemesi, kilit hala tutuluyorken çalışır.
Bu durumu parantezli bir forma dönüştürmek çö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 çalışmasını parantez içine almak ve son noktalı virgülü kaldırmak, akışın ömrünü o açıkça belirtilmiş kapsamla sınırlar. Kapanış parantezinden çıktıktan sonra, kilit serbest bırakılır ve sonraki bir çıkartma çağrısı, aynı dosyayı çakışma olmadan açabilir.
Sonuç
[11:40 - son] Tek satarlıklar yaygın durumları ele alır: tüm klasörü arşivlemek için CreateFromDirectoryAsync ve bunu açmak için ExtractToDirectoryAsync. Arşive girecek dosyalar üzerinde kontrol sağlamanız gerektiğinde, manuel FileStream ve ZipArchive yaklaşımı, giriş düzeyinde filtreleme, yeniden adlandırma ve yol yeniden yazma sağlar.
Özetlemek gerekirse: using System.IO.Compression ekleyin, düz kalemli durumlar için await ZipFile.CreateFromDirectoryAsync veya await ZipFile.ExtractToDirectoryAsync çağrısı yapın ve girişleri filtrelemek veya yeniden adlandırmak gerektiğinde manuel ZipArchive yolunu tercih edin. Akışın, bir sonraki kod çalıştırılmadan önce serbest bırakılması gerekiyorsa using bloklarını karelerle kapsamlayın. Bu eklemeler, .NET 10'da tam zip iş akışında async/await desenlerinin kullanılabilirliğini sağlar.
Canlı kodlama ile takip etmek için Tim Corey's YouTube kanalındaki tam videoyu izleyin.

