From d04830ba909e9682ed8e690b019dd8f58485dc01 Mon Sep 17 00:00:00 2001 From: Adam Hathcock Date: Fri, 2 Jan 2026 17:52:18 +0000 Subject: [PATCH] Add some OpenAsync --- src/SharpCompress/Archives/ArchiveFactory.cs | 126 ++++++++++++++++++ .../Archives/AutoArchiveFactory.cs | 14 ++ .../Archives/GZip/GZipArchive.cs | 64 +++++++++ src/SharpCompress/Archives/IArchiveFactory.cs | 26 ++++ .../Archives/IMultiArchiveFactory.cs | 26 ++++ src/SharpCompress/Archives/Rar/RarArchive.cs | 66 +++++++++ .../Archives/SevenZip/SevenZipArchive.cs | 64 +++++++++ src/SharpCompress/Archives/Tar/TarArchive.cs | 64 +++++++++ src/SharpCompress/Archives/Zip/ZipArchive.cs | 64 +++++++++ src/SharpCompress/Factories/ArcFactory.cs | 11 ++ src/SharpCompress/Factories/ArjFactory.cs | 11 ++ src/SharpCompress/Factories/GZipFactory.cs | 52 ++++++++ src/SharpCompress/Factories/RarFactory.cs | 41 ++++++ .../Factories/SevenZipFactory.cs | 30 +++++ src/SharpCompress/Factories/TarFactory.cs | 52 ++++++++ src/SharpCompress/Factories/ZipFactory.cs | 52 ++++++++ src/SharpCompress/Readers/IReaderFactory.cs | 15 +++ src/SharpCompress/Readers/ReaderFactory.cs | 104 +++++++++++++++ src/SharpCompress/Writers/IWriterFactory.cs | 8 ++ src/SharpCompress/Writers/WriterFactory.cs | 31 +++++ 20 files changed, 921 insertions(+) diff --git a/src/SharpCompress/Archives/ArchiveFactory.cs b/src/SharpCompress/Archives/ArchiveFactory.cs index 94368ece..2a901900 100644 --- a/src/SharpCompress/Archives/ArchiveFactory.cs +++ b/src/SharpCompress/Archives/ArchiveFactory.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Factories; using SharpCompress.IO; @@ -24,6 +26,27 @@ public static class ArchiveFactory return FindFactory(stream).Open(stream, readerOptions); } + /// + /// Opens an Archive for random access asynchronously + /// + /// + /// + /// + /// + public static async Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + readerOptions ??= new ReaderOptions(); + stream = SharpCompressStream.Create(stream, bufferSize: readerOptions.BufferSize); + var factory = FindFactory(stream); + return await factory + .OpenAsync(stream, readerOptions, cancellationToken) + .ConfigureAwait(false); + } + public static IWritableArchive Create(ArchiveType type) { var factory = Factory @@ -49,6 +72,22 @@ public static class ArchiveFactory return Open(new FileInfo(filePath), options); } + /// + /// Opens an Archive from a filepath asynchronously. + /// + /// + /// + /// + public static Task OpenAsync( + string filePath, + ReaderOptions? options = null, + CancellationToken cancellationToken = default + ) + { + filePath.NotNullOrEmpty(nameof(filePath)); + return OpenAsync(new FileInfo(filePath), options, cancellationToken); + } + /// /// Constructor with a FileInfo object to an existing file. /// @@ -61,6 +100,24 @@ public static class ArchiveFactory return FindFactory(fileInfo).Open(fileInfo, options); } + /// + /// Opens an Archive from a FileInfo object asynchronously. + /// + /// + /// + /// + public static async Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? options = null, + CancellationToken cancellationToken = default + ) + { + options ??= new ReaderOptions { LeaveStreamOpen = false }; + + var factory = FindFactory(fileInfo); + return await factory.OpenAsync(fileInfo, options, cancellationToken).ConfigureAwait(false); + } + /// /// Constructor with IEnumerable FileInfo objects, multi and split support. /// @@ -87,6 +144,40 @@ public static class ArchiveFactory return FindFactory(fileInfo).Open(filesArray, options); } + /// + /// Opens a multi-part archive from files asynchronously. + /// + /// + /// + /// + public static async Task OpenAsync( + IEnumerable fileInfos, + ReaderOptions? options = null, + CancellationToken cancellationToken = default + ) + { + fileInfos.NotNull(nameof(fileInfos)); + var filesArray = fileInfos.ToArray(); + if (filesArray.Length == 0) + { + throw new InvalidOperationException("No files to open"); + } + + var fileInfo = filesArray[0]; + if (filesArray.Length == 1) + { + return await OpenAsync(fileInfo, options, cancellationToken).ConfigureAwait(false); + } + + fileInfo.NotNull(nameof(fileInfo)); + options ??= new ReaderOptions { LeaveStreamOpen = false }; + + var factory = FindFactory(fileInfo); + return await factory + .OpenAsync(filesArray, options, cancellationToken) + .ConfigureAwait(false); + } + /// /// Constructor with IEnumerable FileInfo objects, multi and split support. /// @@ -113,6 +204,41 @@ public static class ArchiveFactory return FindFactory(firstStream).Open(streamsArray, options); } + /// + /// Opens a multi-part archive from streams asynchronously. + /// + /// + /// + /// + public static async Task OpenAsync( + IEnumerable streams, + ReaderOptions? options = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + streams.NotNull(nameof(streams)); + var streamsArray = streams.ToArray(); + if (streamsArray.Length == 0) + { + throw new InvalidOperationException("No streams"); + } + + var firstStream = streamsArray[0]; + if (streamsArray.Length == 1) + { + return await OpenAsync(firstStream, options, cancellationToken).ConfigureAwait(false); + } + + firstStream.NotNull(nameof(firstStream)); + options ??= new ReaderOptions(); + + var factory = FindFactory(firstStream); + return await factory + .OpenAsync(streamsArray, options, cancellationToken) + .ConfigureAwait(false); + } + /// /// Extract to specific directory, retaining filename /// diff --git a/src/SharpCompress/Archives/AutoArchiveFactory.cs b/src/SharpCompress/Archives/AutoArchiveFactory.cs index 78313df5..de07c25e 100644 --- a/src/SharpCompress/Archives/AutoArchiveFactory.cs +++ b/src/SharpCompress/Archives/AutoArchiveFactory.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Readers; @@ -25,6 +27,18 @@ class AutoArchiveFactory : IArchiveFactory public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) => ArchiveFactory.Open(stream, readerOptions); + public Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => ArchiveFactory.OpenAsync(stream, readerOptions, cancellationToken); + public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) => ArchiveFactory.Open(fileInfo, readerOptions); + + public Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => ArchiveFactory.OpenAsync(fileInfo, readerOptions, cancellationToken); } diff --git a/src/SharpCompress/Archives/GZip/GZipArchive.cs b/src/SharpCompress/Archives/GZip/GZipArchive.cs index 34e4c648..4871ebb5 100644 --- a/src/SharpCompress/Archives/GZip/GZipArchive.cs +++ b/src/SharpCompress/Archives/GZip/GZipArchive.cs @@ -102,6 +102,70 @@ public class GZipArchive : AbstractWritableArchive ); } + /// + /// Opens a GZipArchive asynchronously from a stream. + /// + /// + /// + /// + public static async Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(stream, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a GZipArchive asynchronously from a FileInfo. + /// + /// + /// + /// + public static async Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(fileInfo, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a GZipArchive asynchronously from multiple streams. + /// + /// + /// + /// + public static async Task OpenAsync( + IReadOnlyList streams, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(streams, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a GZipArchive asynchronously from multiple FileInfo objects. + /// + /// + /// + /// + public static async Task OpenAsync( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(fileInfos, readerOptions)).ConfigureAwait(false); + } + public static GZipArchive Create() => new(); /// diff --git a/src/SharpCompress/Archives/IArchiveFactory.cs b/src/SharpCompress/Archives/IArchiveFactory.cs index 370e5c9f..2ebf5064 100644 --- a/src/SharpCompress/Archives/IArchiveFactory.cs +++ b/src/SharpCompress/Archives/IArchiveFactory.cs @@ -1,4 +1,6 @@ using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Factories; using SharpCompress.Readers; @@ -26,10 +28,34 @@ public interface IArchiveFactory : IFactory /// reading options. IArchive Open(Stream stream, ReaderOptions? readerOptions = null); + /// + /// Opens an Archive for random access asynchronously. + /// + /// An open, readable and seekable stream. + /// reading options. + /// Cancellation token. + Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ); + /// /// Constructor with a FileInfo object to an existing file. /// /// the file to open. /// reading options. IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null); + + /// + /// Opens an Archive from a FileInfo object asynchronously. + /// + /// the file to open. + /// reading options. + /// Cancellation token. + Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ); } diff --git a/src/SharpCompress/Archives/IMultiArchiveFactory.cs b/src/SharpCompress/Archives/IMultiArchiveFactory.cs index c26b649f..d736bf84 100644 --- a/src/SharpCompress/Archives/IMultiArchiveFactory.cs +++ b/src/SharpCompress/Archives/IMultiArchiveFactory.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Factories; using SharpCompress.Readers; @@ -27,10 +29,34 @@ public interface IMultiArchiveFactory : IFactory /// reading options. IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null); + /// + /// Opens a multi-part archive from streams asynchronously. + /// + /// + /// reading options. + /// Cancellation token. + Task OpenAsync( + IReadOnlyList streams, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ); + /// /// Constructor with IEnumerable Stream objects, multi and split support. /// /// /// reading options. IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null); + + /// + /// Opens a multi-part archive from files asynchronously. + /// + /// + /// reading options. + /// Cancellation token. + Task OpenAsync( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ); } diff --git a/src/SharpCompress/Archives/Rar/RarArchive.cs b/src/SharpCompress/Archives/Rar/RarArchive.cs index 9acfdccc..9fa8e2a1 100644 --- a/src/SharpCompress/Archives/Rar/RarArchive.cs +++ b/src/SharpCompress/Archives/Rar/RarArchive.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Common.Rar; using SharpCompress.Common.Rar.Headers; @@ -181,6 +183,70 @@ public class RarArchive : AbstractArchive ); } + /// + /// Opens a RarArchive asynchronously from a stream. + /// + /// + /// + /// + public static async Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(stream, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a RarArchive asynchronously from a FileInfo. + /// + /// + /// + /// + public static async Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(fileInfo, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a RarArchive asynchronously from multiple streams. + /// + /// + /// + /// + public static async Task OpenAsync( + IReadOnlyList streams, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(streams, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a RarArchive asynchronously from multiple FileInfo objects. + /// + /// + /// + /// + public static async Task OpenAsync( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(fileInfos, readerOptions)).ConfigureAwait(false); + } + public static bool IsRarFile(string filePath) => IsRarFile(new FileInfo(filePath)); public static bool IsRarFile(FileInfo fileInfo) diff --git a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs index d9b5ba1a..a3041c7d 100644 --- a/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs +++ b/src/SharpCompress/Archives/SevenZip/SevenZipArchive.cs @@ -105,6 +105,70 @@ public class SevenZipArchive : AbstractArchive + /// Opens a SevenZipArchive asynchronously from a stream. + /// + /// + /// + /// + public static async Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(stream, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a SevenZipArchive asynchronously from a FileInfo. + /// + /// + /// + /// + public static async Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(fileInfo, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a SevenZipArchive asynchronously from multiple streams. + /// + /// + /// + /// + public static async Task OpenAsync( + IReadOnlyList streams, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(streams, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a SevenZipArchive asynchronously from multiple FileInfo objects. + /// + /// + /// + /// + public static async Task OpenAsync( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(fileInfos, readerOptions)).ConfigureAwait(false); + } + /// /// Constructor with a SourceStream able to handle FileInfo and Streams. /// diff --git a/src/SharpCompress/Archives/Tar/TarArchive.cs b/src/SharpCompress/Archives/Tar/TarArchive.cs index 2754fd9b..11a7c0cc 100644 --- a/src/SharpCompress/Archives/Tar/TarArchive.cs +++ b/src/SharpCompress/Archives/Tar/TarArchive.cs @@ -103,6 +103,70 @@ public class TarArchive : AbstractWritableArchive ); } + /// + /// Opens a TarArchive asynchronously from a stream. + /// + /// + /// + /// + public static async Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(stream, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a TarArchive asynchronously from a FileInfo. + /// + /// + /// + /// + public static async Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(fileInfo, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a TarArchive asynchronously from multiple streams. + /// + /// + /// + /// + public static async Task OpenAsync( + IReadOnlyList streams, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(streams, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a TarArchive asynchronously from multiple FileInfo objects. + /// + /// + /// + /// + public static async Task OpenAsync( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(fileInfos, readerOptions)).ConfigureAwait(false); + } + public static bool IsTarFile(string filePath) => IsTarFile(new FileInfo(filePath)); public static bool IsTarFile(FileInfo fileInfo) diff --git a/src/SharpCompress/Archives/Zip/ZipArchive.cs b/src/SharpCompress/Archives/Zip/ZipArchive.cs index ae1a58e5..1395505c 100644 --- a/src/SharpCompress/Archives/Zip/ZipArchive.cs +++ b/src/SharpCompress/Archives/Zip/ZipArchive.cs @@ -124,6 +124,70 @@ public class ZipArchive : AbstractWritableArchive ); } + /// + /// Opens a ZipArchive asynchronously from a stream. + /// + /// + /// + /// + public static async Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(stream, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a ZipArchive asynchronously from a FileInfo. + /// + /// + /// + /// + public static async Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(fileInfo, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a ZipArchive asynchronously from multiple streams. + /// + /// + /// + /// + public static async Task OpenAsync( + IReadOnlyList streams, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(streams, readerOptions)).ConfigureAwait(false); + } + + /// + /// Opens a ZipArchive asynchronously from multiple FileInfo objects. + /// + /// + /// + /// + public static async Task OpenAsync( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(fileInfos, readerOptions)).ConfigureAwait(false); + } + public static bool IsZipFile( string filePath, string? password = null, diff --git a/src/SharpCompress/Factories/ArcFactory.cs b/src/SharpCompress/Factories/ArcFactory.cs index b5180afa..18065afd 100644 --- a/src/SharpCompress/Factories/ArcFactory.cs +++ b/src/SharpCompress/Factories/ArcFactory.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; +using System.Threading; using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Readers; @@ -42,5 +43,15 @@ namespace SharpCompress.Factories public IReader OpenReader(Stream stream, ReaderOptions? options) => ArcReader.Open(stream, options); + + public async Task OpenReaderAsync( + Stream stream, + ReaderOptions? options, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(OpenReader(stream, options)).ConfigureAwait(false); + } } } diff --git a/src/SharpCompress/Factories/ArjFactory.cs b/src/SharpCompress/Factories/ArjFactory.cs index 7499946b..b0b120f3 100644 --- a/src/SharpCompress/Factories/ArjFactory.cs +++ b/src/SharpCompress/Factories/ArjFactory.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using System.Threading; using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Common.Arj.Headers; @@ -38,5 +39,15 @@ namespace SharpCompress.Factories public IReader OpenReader(Stream stream, ReaderOptions? options) => ArjReader.Open(stream, options); + + public async Task OpenReaderAsync( + Stream stream, + ReaderOptions? options, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(OpenReader(stream, options)).ConfigureAwait(false); + } } } diff --git a/src/SharpCompress/Factories/GZipFactory.cs b/src/SharpCompress/Factories/GZipFactory.cs index 17f344cf..923991b7 100644 --- a/src/SharpCompress/Factories/GZipFactory.cs +++ b/src/SharpCompress/Factories/GZipFactory.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Archives; using SharpCompress.Archives.GZip; using SharpCompress.Archives.Tar; @@ -54,10 +56,24 @@ public class GZipFactory public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) => GZipArchive.Open(stream, readerOptions); + /// + public Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => GZipArchive.OpenAsync(stream, readerOptions, cancellationToken); + /// public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) => GZipArchive.Open(fileInfo, readerOptions); + /// + public Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => GZipArchive.OpenAsync(fileInfo, readerOptions, cancellationToken); + #endregion #region IMultiArchiveFactory @@ -66,10 +82,24 @@ public class GZipFactory public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) => GZipArchive.Open(streams, readerOptions); + /// + public Task OpenAsync( + IReadOnlyList streams, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => GZipArchive.OpenAsync(streams, readerOptions, cancellationToken); + /// public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) => GZipArchive.Open(fileInfos, readerOptions); + /// + public Task OpenAsync( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => GZipArchive.OpenAsync(fileInfos, readerOptions, cancellationToken); + #endregion #region IReaderFactory @@ -108,6 +138,17 @@ public class GZipFactory public IReader OpenReader(Stream stream, ReaderOptions? options) => GZipReader.Open(stream, options); + /// + public async Task OpenReaderAsync( + Stream stream, + ReaderOptions? options, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(OpenReader(stream, options)).ConfigureAwait(false); + } + #endregion #region IWriterFactory @@ -122,6 +163,17 @@ public class GZipFactory return new GZipWriter(stream, new GZipWriterOptions(writerOptions)); } + /// + public async Task OpenAsync( + Stream stream, + WriterOptions writerOptions, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(stream, writerOptions)).ConfigureAwait(false); + } + #endregion #region IWriteableArchiveFactory diff --git a/src/SharpCompress/Factories/RarFactory.cs b/src/SharpCompress/Factories/RarFactory.cs index 61099905..2986e61b 100644 --- a/src/SharpCompress/Factories/RarFactory.cs +++ b/src/SharpCompress/Factories/RarFactory.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Archives; using SharpCompress.Archives.Rar; using SharpCompress.Common; @@ -47,10 +49,24 @@ public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReade public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) => RarArchive.Open(stream, readerOptions); + /// + public Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => RarArchive.OpenAsync(stream, readerOptions, cancellationToken); + /// public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) => RarArchive.Open(fileInfo, readerOptions); + /// + public Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => RarArchive.OpenAsync(fileInfo, readerOptions, cancellationToken); + #endregion #region IMultiArchiveFactory @@ -59,10 +75,24 @@ public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReade public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) => RarArchive.Open(streams, readerOptions); + /// + public Task OpenAsync( + IReadOnlyList streams, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => RarArchive.OpenAsync(streams, readerOptions, cancellationToken); + /// public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) => RarArchive.Open(fileInfos, readerOptions); + /// + public Task OpenAsync( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => RarArchive.OpenAsync(fileInfos, readerOptions, cancellationToken); + #endregion #region IReaderFactory @@ -71,5 +101,16 @@ public class RarFactory : Factory, IArchiveFactory, IMultiArchiveFactory, IReade public IReader OpenReader(Stream stream, ReaderOptions? options) => RarReader.Open(stream, options); + /// + public async Task OpenReaderAsync( + Stream stream, + ReaderOptions? options, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(OpenReader(stream, options)).ConfigureAwait(false); + } + #endregion } diff --git a/src/SharpCompress/Factories/SevenZipFactory.cs b/src/SharpCompress/Factories/SevenZipFactory.cs index 18dedbfd..a9c74fae 100644 --- a/src/SharpCompress/Factories/SevenZipFactory.cs +++ b/src/SharpCompress/Factories/SevenZipFactory.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Archives; using SharpCompress.Archives.SevenZip; using SharpCompress.Common; @@ -42,10 +44,24 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) => SevenZipArchive.Open(stream, readerOptions); + /// + public Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => SevenZipArchive.OpenAsync(stream, readerOptions, cancellationToken); + /// public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) => SevenZipArchive.Open(fileInfo, readerOptions); + /// + public Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => SevenZipArchive.OpenAsync(fileInfo, readerOptions, cancellationToken); + #endregion #region IMultiArchiveFactory @@ -54,10 +70,24 @@ public class SevenZipFactory : Factory, IArchiveFactory, IMultiArchiveFactory public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) => SevenZipArchive.Open(streams, readerOptions); + /// + public Task OpenAsync( + IReadOnlyList streams, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => SevenZipArchive.OpenAsync(streams, readerOptions, cancellationToken); + /// public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) => SevenZipArchive.Open(fileInfos, readerOptions); + /// + public Task OpenAsync( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => SevenZipArchive.OpenAsync(fileInfos, readerOptions, cancellationToken); + #endregion #region reader diff --git a/src/SharpCompress/Factories/TarFactory.cs b/src/SharpCompress/Factories/TarFactory.cs index d32020fd..240b835d 100644 --- a/src/SharpCompress/Factories/TarFactory.cs +++ b/src/SharpCompress/Factories/TarFactory.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Archives; using SharpCompress.Archives.Tar; using SharpCompress.Common; @@ -67,10 +69,24 @@ public class TarFactory public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) => TarArchive.Open(stream, readerOptions); + /// + public Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => TarArchive.OpenAsync(stream, readerOptions, cancellationToken); + /// public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) => TarArchive.Open(fileInfo, readerOptions); + /// + public Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => TarArchive.OpenAsync(fileInfo, readerOptions, cancellationToken); + #endregion #region IMultiArchiveFactory @@ -79,10 +95,24 @@ public class TarFactory public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) => TarArchive.Open(streams, readerOptions); + /// + public Task OpenAsync( + IReadOnlyList streams, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => TarArchive.OpenAsync(streams, readerOptions, cancellationToken); + /// public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) => TarArchive.Open(fileInfos, readerOptions); + /// + public Task OpenAsync( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => TarArchive.OpenAsync(fileInfos, readerOptions, cancellationToken); + #endregion #region IReaderFactory @@ -234,6 +264,17 @@ public class TarFactory public IReader OpenReader(Stream stream, ReaderOptions? options) => TarReader.Open(stream, options); + /// + public async Task OpenReaderAsync( + Stream stream, + ReaderOptions? options, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(OpenReader(stream, options)).ConfigureAwait(false); + } + #endregion #region IWriterFactory @@ -242,6 +283,17 @@ public class TarFactory public IWriter Open(Stream stream, WriterOptions writerOptions) => new TarWriter(stream, new TarWriterOptions(writerOptions)); + /// + public async Task OpenAsync( + Stream stream, + WriterOptions writerOptions, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(stream, writerOptions)).ConfigureAwait(false); + } + #endregion #region IWriteableArchiveFactory diff --git a/src/SharpCompress/Factories/ZipFactory.cs b/src/SharpCompress/Factories/ZipFactory.cs index 5c2fcad8..30f4b49b 100644 --- a/src/SharpCompress/Factories/ZipFactory.cs +++ b/src/SharpCompress/Factories/ZipFactory.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Archives; using SharpCompress.Archives.Zip; using SharpCompress.Common; @@ -91,10 +93,24 @@ public class ZipFactory public IArchive Open(Stream stream, ReaderOptions? readerOptions = null) => ZipArchive.Open(stream, readerOptions); + /// + public Task OpenAsync( + Stream stream, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => ZipArchive.OpenAsync(stream, readerOptions, cancellationToken); + /// public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null) => ZipArchive.Open(fileInfo, readerOptions); + /// + public Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => ZipArchive.OpenAsync(fileInfo, readerOptions, cancellationToken); + #endregion #region IMultiArchiveFactory @@ -103,10 +119,24 @@ public class ZipFactory public IArchive Open(IReadOnlyList streams, ReaderOptions? readerOptions = null) => ZipArchive.Open(streams, readerOptions); + /// + public Task OpenAsync( + IReadOnlyList streams, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => ZipArchive.OpenAsync(streams, readerOptions, cancellationToken); + /// public IArchive Open(IReadOnlyList fileInfos, ReaderOptions? readerOptions = null) => ZipArchive.Open(fileInfos, readerOptions); + /// + public Task OpenAsync( + IReadOnlyList fileInfos, + ReaderOptions? readerOptions = null, + CancellationToken cancellationToken = default + ) => ZipArchive.OpenAsync(fileInfos, readerOptions, cancellationToken); + #endregion #region IReaderFactory @@ -115,6 +145,17 @@ public class ZipFactory public IReader OpenReader(Stream stream, ReaderOptions? options) => ZipReader.Open(stream, options); + /// + public async Task OpenReaderAsync( + Stream stream, + ReaderOptions? options, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(OpenReader(stream, options)).ConfigureAwait(false); + } + #endregion #region IWriterFactory @@ -123,6 +164,17 @@ public class ZipFactory public IWriter Open(Stream stream, WriterOptions writerOptions) => new ZipWriter(stream, new ZipWriterOptions(writerOptions)); + /// + public async Task OpenAsync( + Stream stream, + WriterOptions writerOptions, + CancellationToken cancellationToken = default + ) + { + cancellationToken.ThrowIfCancellationRequested(); + return await Task.FromResult(Open(stream, writerOptions)).ConfigureAwait(false); + } + #endregion #region IWriteableArchiveFactory diff --git a/src/SharpCompress/Readers/IReaderFactory.cs b/src/SharpCompress/Readers/IReaderFactory.cs index 08c190a3..dd95f187 100644 --- a/src/SharpCompress/Readers/IReaderFactory.cs +++ b/src/SharpCompress/Readers/IReaderFactory.cs @@ -1,4 +1,6 @@ using System.IO; +using System.Threading; +using System.Threading.Tasks; namespace SharpCompress.Readers; @@ -11,4 +13,17 @@ public interface IReaderFactory : Factories.IFactory /// /// IReader OpenReader(Stream stream, ReaderOptions? options); + + /// + /// Opens a Reader asynchronously for Non-seeking usage + /// + /// + /// + /// + /// + Task OpenReaderAsync( + Stream stream, + ReaderOptions? options, + CancellationToken cancellationToken = default + ); } diff --git a/src/SharpCompress/Readers/ReaderFactory.cs b/src/SharpCompress/Readers/ReaderFactory.cs index 80995e41..6809cfa4 100644 --- a/src/SharpCompress/Readers/ReaderFactory.cs +++ b/src/SharpCompress/Readers/ReaderFactory.cs @@ -1,6 +1,8 @@ using System; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; using SharpCompress.Factories; using SharpCompress.IO; @@ -15,12 +17,46 @@ public static class ReaderFactory return Open(new FileInfo(filePath), options); } + /// + /// Opens a Reader from a filepath asynchronously + /// + /// + /// + /// + /// + public static Task OpenAsync( + string filePath, + ReaderOptions? options = null, + CancellationToken cancellationToken = default + ) + { + filePath.NotNullOrEmpty(nameof(filePath)); + return OpenAsync(new FileInfo(filePath), options, cancellationToken); + } + public static IReader Open(FileInfo fileInfo, ReaderOptions? options = null) { options ??= new ReaderOptions { LeaveStreamOpen = false }; return Open(fileInfo.OpenRead(), options); } + /// + /// Opens a Reader from a FileInfo asynchronously + /// + /// + /// + /// + /// + public static Task OpenAsync( + FileInfo fileInfo, + ReaderOptions? options = null, + CancellationToken cancellationToken = default + ) + { + options ??= new ReaderOptions { LeaveStreamOpen = false }; + return OpenAsync(fileInfo.OpenRead(), options, cancellationToken); + } + /// /// Opens a Reader for Non-seeking usage /// @@ -73,4 +109,72 @@ public static class ReaderFactory "Cannot determine compressed stream type. Supported Reader Formats: Arc, Arj, Zip, GZip, BZip2, Tar, Rar, LZip, XZ, ZStandard" ); } + + /// + /// Opens a Reader for Non-seeking usage asynchronously + /// + /// + /// + /// + /// + public static async Task OpenAsync( + Stream stream, + ReaderOptions? options = null, + CancellationToken cancellationToken = default + ) + { + stream.NotNull(nameof(stream)); + options ??= new ReaderOptions() { LeaveStreamOpen = false }; + + var bStream = new SharpCompressStream(stream, bufferSize: options.BufferSize); + + long pos = ((IStreamStack)bStream).GetPosition(); + + var factories = Factories.Factory.Factories.OfType(); + + Factory? testedFactory = null; + + if (!string.IsNullOrWhiteSpace(options.ExtensionHint)) + { + testedFactory = factories.FirstOrDefault(a => + a.GetSupportedExtensions() + .Contains(options.ExtensionHint, StringComparer.CurrentCultureIgnoreCase) + ); + if (testedFactory is IReaderFactory readerFactory) + { + ((IStreamStack)bStream).StackSeek(pos); + if (testedFactory.IsArchive(bStream, options.Password, options.BufferSize)) + { + ((IStreamStack)bStream).StackSeek(pos); + return await readerFactory + .OpenReaderAsync(bStream, options, cancellationToken) + .ConfigureAwait(false); + } + } + ((IStreamStack)bStream).StackSeek(pos); + } + + foreach (var factory in factories) + { + if (testedFactory == factory) + { + continue; // Already tested above + } + ((IStreamStack)bStream).StackSeek(pos); + if ( + factory is IReaderFactory readerFactory + && factory.IsArchive(bStream, options.Password, options.BufferSize) + ) + { + ((IStreamStack)bStream).StackSeek(pos); + return await readerFactory + .OpenReaderAsync(bStream, options, cancellationToken) + .ConfigureAwait(false); + } + } + + throw new InvalidFormatException( + "Cannot determine compressed stream type. Supported Reader Formats: Arc, Arj, Zip, GZip, BZip2, Tar, Rar, LZip, XZ, ZStandard" + ); + } } diff --git a/src/SharpCompress/Writers/IWriterFactory.cs b/src/SharpCompress/Writers/IWriterFactory.cs index 094a5553..059dbe59 100644 --- a/src/SharpCompress/Writers/IWriterFactory.cs +++ b/src/SharpCompress/Writers/IWriterFactory.cs @@ -1,4 +1,6 @@ using System.IO; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Factories; namespace SharpCompress.Writers; @@ -6,4 +8,10 @@ namespace SharpCompress.Writers; public interface IWriterFactory : IFactory { IWriter Open(Stream stream, WriterOptions writerOptions); + + Task OpenAsync( + Stream stream, + WriterOptions writerOptions, + CancellationToken cancellationToken = default + ); } diff --git a/src/SharpCompress/Writers/WriterFactory.cs b/src/SharpCompress/Writers/WriterFactory.cs index 50e1fdfc..518e9d10 100644 --- a/src/SharpCompress/Writers/WriterFactory.cs +++ b/src/SharpCompress/Writers/WriterFactory.cs @@ -1,6 +1,8 @@ using System; using System.IO; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using SharpCompress.Common; namespace SharpCompress.Writers; @@ -20,4 +22,33 @@ public static class WriterFactory throw new NotSupportedException("Archive Type does not have a Writer: " + archiveType); } + + /// + /// Opens a Writer asynchronously. + /// + /// The stream to write to. + /// The archive type. + /// Writer options. + /// Cancellation token. + /// A task that returns an IWriter. + public static async Task OpenAsync( + Stream stream, + ArchiveType archiveType, + WriterOptions writerOptions, + CancellationToken cancellationToken = default + ) + { + var factory = Factories + .Factory.Factories.OfType() + .FirstOrDefault(item => item.KnownArchiveType == archiveType); + + if (factory != null) + { + return await factory + .OpenAsync(stream, writerOptions, cancellationToken) + .ConfigureAwait(false); + } + + throw new NotSupportedException("Archive Type does not have a Writer: " + archiveType); + } }