From c2e01798f8bbe63fd4c3568d8e2d594d7c504ae9 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Fri, 19 Dec 2025 14:09:01 +0000 Subject: [PATCH] refactor archive extensions --- .../Archives/IArchiveExtensions.cs | 235 ++++++++++-------- 1 file changed, 129 insertions(+), 106 deletions(-) diff --git a/src/SharpCompress/Archives/IArchiveExtensions.cs b/src/SharpCompress/Archives/IArchiveExtensions.cs index 00aae468..0d39c6e2 100644 --- a/src/SharpCompress/Archives/IArchiveExtensions.cs +++ b/src/SharpCompress/Archives/IArchiveExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Threading; using System.Threading.Tasks; using SharpCompress.Common; @@ -11,135 +10,159 @@ namespace SharpCompress.Archives; public static class IArchiveExtensions { - /// - /// Extract to specific directory, retaining filename - /// - public static void WriteToDirectory( - this IArchive archive, - string destinationDirectory, - ExtractionOptions? options = null - ) + /// The archive to extract. + extension(IArchive archive) { - // For solid archives (Rar, 7Zip), use the optimized reader-based approach - if (archive.IsSolid || archive.Type == ArchiveType.SevenZip) + /// + /// Extract to specific directory with progress reporting + /// + /// The folder to extract into. + /// Extraction options. + /// Optional progress reporter for tracking extraction progress. + public void WriteToDirectory( + string destinationDirectory, + ExtractionOptions? options = null, + IProgress? progress = null + ) { - using var reader = archive.ExtractAllEntries(); - reader.WriteAllToDirectory(destinationDirectory, options); + // For solid archives (Rar, 7Zip), use the optimized reader-based approach + if (archive.IsSolid || archive.Type == ArchiveType.SevenZip) + { + using var reader = archive.ExtractAllEntries(); + reader.WriteAllToDirectory(destinationDirectory, options); + } + else + { + // For non-solid archives, extract entries directly + archive.WriteToDirectoryInternal(destinationDirectory, options, progress); + } } - else + + private void WriteToDirectoryInternal( + string destinationDirectory, + ExtractionOptions? options, + IProgress? progress + ) { - // For non-solid archives, extract entries directly + // Prepare for progress reporting + var totalBytes = archive.TotalUncompressSize; + var bytesRead = 0L; + + // Tracking for created directories. + var seenDirectories = new HashSet(); + + // Extract foreach (var entry in archive.Entries) { - if (!entry.IsDirectory) + if (entry.IsDirectory) { - entry.WriteToDirectory(destinationDirectory, options); + var dirPath = Path.Combine( + destinationDirectory, + entry.Key.NotNull("Entry Key is null") + ); + if ( + Path.GetDirectoryName(dirPath + "/") is { } parentDirectory + && seenDirectories.Add(dirPath) + ) + { + Directory.CreateDirectory(parentDirectory); + } + continue; } + + // Use the entry's WriteToDirectory method which respects ExtractionOptions + entry.WriteToDirectory(destinationDirectory, options); + + // Update progress + bytesRead += entry.Size; + progress?.Report( + new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes) + ); } } - } - /// - /// Extract to specific directory with progress reporting - /// - /// The archive to extract. - /// The folder to extract into. - /// Extraction options. - /// Optional progress reporter for tracking extraction progress. - public static void WriteToDirectory( - this IArchive archive, - string destinationDirectory, - ExtractionOptions? options, - IProgress? progress - ) - { - // Prepare for progress reporting - var totalBytes = archive.TotalUncompressSize; - var bytesRead = 0L; - - // Tracking for created directories. - var seenDirectories = new HashSet(); - - // Extract - foreach (var entry in archive.Entries) + /// + /// Extract to specific directory asynchronously with progress reporting and cancellation support + /// + /// The folder to extract into. + /// Extraction options. + /// Optional progress reporter for tracking extraction progress. + /// Optional cancellation token. + public async Task WriteToDirectoryAsync( + string destinationDirectory, + ExtractionOptions? options = null, + IProgress? progress = null, + CancellationToken cancellationToken = default + ) { - if (entry.IsDirectory) + // For solid archives (Rar, 7Zip), use the optimized reader-based approach + if (archive.IsSolid || archive.Type == ArchiveType.SevenZip) { - var dirPath = Path.Combine( + using var reader = archive.ExtractAllEntries(); + await reader.WriteAllToDirectoryAsync( destinationDirectory, - entry.Key.NotNull("Entry Key is null") + options, + cancellationToken + ); + } + else + { + // For non-solid archives, extract entries directly + await archive.WriteToDirectoryAsyncInternal( + destinationDirectory, + options, + progress, + cancellationToken ); - if ( - Path.GetDirectoryName(dirPath + "/") is { } parentDirectory - && seenDirectories.Add(dirPath) - ) - { - Directory.CreateDirectory(parentDirectory); - } - continue; } - - // Use the entry's WriteToDirectory method which respects ExtractionOptions - entry.WriteToDirectory(destinationDirectory, options); - - // Update progress - bytesRead += entry.Size; - progress?.Report(new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes)); } - } - /// - /// Extract to specific directory asynchronously with progress reporting and cancellation support - /// - /// The archive to extract. - /// The folder to extract into. - /// Extraction options. - /// Optional progress reporter for tracking extraction progress. - /// Optional cancellation token. - public static async Task WriteToDirectoryAsync( - this IArchive archive, - string destinationDirectory, - ExtractionOptions? options = null, - IProgress? progress = null, - CancellationToken cancellationToken = default - ) - { - // Prepare for progress reporting - var totalBytes = archive.TotalUncompressSize; - var bytesRead = 0L; - - // Tracking for created directories. - var seenDirectories = new HashSet(); - - // Extract - foreach (var entry in archive.Entries) + private async Task WriteToDirectoryAsyncInternal( + string destinationDirectory, + ExtractionOptions? options, + IProgress? progress, + CancellationToken cancellationToken + ) { - cancellationToken.ThrowIfCancellationRequested(); + // Prepare for progress reporting + var totalBytes = archive.TotalUncompressSize; + var bytesRead = 0L; - if (entry.IsDirectory) + // Tracking for created directories. + var seenDirectories = new HashSet(); + + // Extract + foreach (var entry in archive.Entries) { - var dirPath = Path.Combine( - destinationDirectory, - entry.Key.NotNull("Entry Key is null") - ); - if ( - Path.GetDirectoryName(dirPath + "/") is { } parentDirectory - && seenDirectories.Add(dirPath) - ) + cancellationToken.ThrowIfCancellationRequested(); + + if (entry.IsDirectory) { - Directory.CreateDirectory(parentDirectory); + var dirPath = Path.Combine( + destinationDirectory, + entry.Key.NotNull("Entry Key is null") + ); + if ( + Path.GetDirectoryName(dirPath + "/") is { } parentDirectory + && seenDirectories.Add(dirPath) + ) + { + Directory.CreateDirectory(parentDirectory); + } + continue; } - continue; + + // Use the entry's WriteToDirectoryAsync method which respects ExtractionOptions + await entry + .WriteToDirectoryAsync(destinationDirectory, options, cancellationToken) + .ConfigureAwait(false); + + // Update progress + bytesRead += entry.Size; + progress?.Report( + new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes) + ); } - - // Use the entry's WriteToDirectoryAsync method which respects ExtractionOptions - await entry - .WriteToDirectoryAsync(destinationDirectory, options, cancellationToken) - .ConfigureAwait(false); - - // Update progress - bytesRead += entry.Size; - progress?.Report(new ProgressReport(entry.Key ?? string.Empty, bytesRead, totalBytes)); } } }