Merge pull request #709 from vpenades/master

Generalized factories to readers and writers.
This commit is contained in:
Adam Hathcock
2022-12-06 14:17:02 +00:00
committed by GitHub
20 changed files with 990 additions and 514 deletions

View File

@@ -8,6 +8,7 @@ using SharpCompress.Archives.SevenZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Factories;
using SharpCompress.IO;
using SharpCompress.Readers;
@@ -15,36 +16,6 @@ namespace SharpCompress.Archives
{
public static class ArchiveFactory
{
private static readonly HashSet<IArchiveFactory> archiveFactories;
/// <summary>
/// Gets the collection of registered archive factories
/// </summary>
public static IReadOnlyCollection<IArchiveFactory> Factories => archiveFactories;
static ArchiveFactory()
{
archiveFactories = new HashSet<IArchiveFactory>();
RegisterFactory(new Zip.ZipArchiveFactory());
RegisterFactory(new Rar.RarArchiveFactory());
RegisterFactory(new SevenZip.SevenZipArchiveFactory());
RegisterFactory(new GZip.GZipArchiveFactory());
RegisterFactory(new Tar.TarArchiveFactory());
}
/// <summary>
/// Registers an archive factory.
/// </summary>
/// <param name="factory">The factory to register.</param>
/// <exception cref="ArgumentNullException"><paramref name="factory"/> must not be null.</exception>
public static void RegisterFactory(IArchiveFactory factory)
{
factory.CheckNotNull(nameof(factory));
archiveFactories.Add(factory);
}
/// <summary>
/// Opens an Archive for random access
/// </summary>
@@ -53,26 +24,23 @@ namespace SharpCompress.Archives
/// <returns></returns>
public static IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
stream.CheckNotNull(nameof(stream));
if (!stream.CanRead || !stream.CanSeek)
{
throw new ArgumentException("Stream should be readable and seekable");
}
readerOptions ??= new ReaderOptions();
return FindArchiveFactory(stream).Open(stream, readerOptions);
return FindFactory<IArchiveFactory>(stream).Open(stream, readerOptions);
}
public static IWritableArchive Create(ArchiveType type)
{
return type switch
{
ArchiveType.Zip => ZipArchive.Create(),
ArchiveType.Tar => TarArchive.Create(),
ArchiveType.GZip => GZipArchive.Create(),
_ => throw new NotSupportedException("Cannot create Archives of type: " + type)
};
var factory = Factories.Factory
.Factories
.OfType<IWriteableArchiveFactory>()
.Where(item => item.KnownArchiveType == type)
.FirstOrDefault();
if (factory != null)
return factory.CreateWriteableArchive();
throw new NotSupportedException("Cannot create Archives of type: " + type);
}
/// <summary>
@@ -92,11 +60,10 @@ namespace SharpCompress.Archives
/// <param name="fileInfo"></param>
/// <param name="options"></param>
public static IArchive Open(FileInfo fileInfo, ReaderOptions? options = null)
{
fileInfo.CheckNotNull(nameof(fileInfo));
{
options ??= new ReaderOptions { LeaveStreamOpen = false };
return FindArchiveFactory(fileInfo).Open(fileInfo, options);
return FindFactory<IArchiveFactory>(fileInfo).Open(fileInfo, options);
}
/// <summary>
@@ -107,17 +74,17 @@ namespace SharpCompress.Archives
public static IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? options = null)
{
fileInfos.CheckNotNull(nameof(fileInfos));
FileInfo[] files = fileInfos.ToArray();
if (files.Length == 0)
FileInfo[] filesArray = fileInfos.ToArray();
if (filesArray.Length == 0)
throw new InvalidOperationException("No files to open");
FileInfo fileInfo = files[0];
if (files.Length == 1)
FileInfo fileInfo = filesArray[0];
if (filesArray.Length == 1)
return Open(fileInfo, options);
fileInfo.CheckNotNull(nameof(fileInfo));
options ??= new ReaderOptions { LeaveStreamOpen = false };
return FindArchiveFactory(fileInfo).Open(fileInfos, options);
return FindFactory<IMultiArchiveFactory>(fileInfo).Open(filesArray, options);
}
/// <summary>
@@ -138,7 +105,7 @@ namespace SharpCompress.Archives
firstStream.CheckNotNull(nameof(firstStream));
options ??= new ReaderOptions();
return FindArchiveFactory(firstStream).Open(streamsArray, options);
return FindFactory<IMultiArchiveFactory>(firstStream).Open(streamsArray, options);
}
/// <summary>
@@ -154,16 +121,18 @@ namespace SharpCompress.Archives
}
}
private static IArchiveFactory FindArchiveFactory(FileInfo finfo)
private static T FindFactory<T>(FileInfo finfo)
where T : IFactory
{
finfo.CheckNotNull(nameof(finfo));
using (Stream stream = finfo.OpenRead())
{
return FindArchiveFactory(stream);
return FindFactory<T>(stream);
}
}
private static IArchiveFactory FindArchiveFactory(Stream stream)
private static T FindFactory<T>(Stream stream)
where T: IFactory
{
stream.CheckNotNull(nameof(stream));
if (!stream.CanRead || !stream.CanSeek)
@@ -171,9 +140,11 @@ namespace SharpCompress.Archives
throw new ArgumentException("Stream should be readable and seekable");
}
var factories = Factories.Factory.Factories.OfType<T>();
long startPosition = stream.Position;
foreach (var factory in Factories)
foreach (var factory in factories)
{
stream.Seek(startPosition, SeekOrigin.Begin);
@@ -185,9 +156,7 @@ namespace SharpCompress.Archives
}
}
var extensions = Factories
.Select(item => item.Name)
.Aggregate((a, b) => a + ", " + b);
var extensions = string.Join(", ", factories.Select(item => item.Name));
throw new InvalidOperationException($"Cannot determine compressed stream type. Supported Archive Formats: {extensions}");
}
@@ -208,41 +177,21 @@ namespace SharpCompress.Archives
{
throw new ArgumentException("Stream should be readable and seekable");
}
if (ZipArchive.IsZipFile(stream, null))
type = ArchiveType.Zip;
stream.Seek(0, SeekOrigin.Begin);
if (type == null)
var startPosition = stream.Position;
foreach(var factory in Factories.Factory.Factories)
{
if (SevenZipArchive.IsSevenZipFile(stream))
type = ArchiveType.SevenZip;
stream.Seek(0, SeekOrigin.Begin);
}
if (type == null)
{
if (GZipArchive.IsGZipFile(stream))
type = ArchiveType.GZip;
stream.Seek(0, SeekOrigin.Begin);
}
if (type == null)
{
if (RarArchive.IsRarFile(stream))
type = ArchiveType.Rar;
stream.Seek(0, SeekOrigin.Begin);
}
if (type == null)
{
if (TarArchive.IsTarFile(stream))
type = ArchiveType.Tar;
stream.Seek(0, SeekOrigin.Begin);
}
if (type == null) //test multipartzip as it could find zips in other non compressed archive types?
{
if (ZipArchive.IsZipMulti(stream)) //test the zip (last) file of a multipart zip
type = ArchiveType.Zip;
stream.Seek(0, SeekOrigin.Begin);
stream.Position = startPosition;
if (factory.IsArchive(stream, null))
{
type = factory.KnownArchiveType;
return true;
}
}
return type != null;
return false;
}
/// <summary>
@@ -264,21 +213,21 @@ namespace SharpCompress.Archives
public static IEnumerable<FileInfo> GetFileParts(FileInfo part1)
{
part1.CheckNotNull(nameof(part1));
yield return part1;
int i = 1;
yield return part1;
FileInfo? part = RarArchiveVolumeFactory.GetFilePart(i++, part1);
if (part != null)
foreach(var factory in Factory.Factories.OfType<IFactory>())
{
yield return part;
while ((part = RarArchiveVolumeFactory.GetFilePart(i++, part1)) != null) //tests split too
yield return part;
}
else
{
i = 1;
while ((part = ZipArchiveVolumeFactory.GetFilePart(i++, part1)) != null) //tests split too
int i = 1;
FileInfo? part = factory.GetFilePart(i++, part1);
if (part != null)
{
yield return part;
while ((part = factory.GetFilePart(i++, part1)) != null) //tests split too
yield return part;
yield break;
}
}
}
}

View File

@@ -1,51 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Readers;
namespace SharpCompress.Archives.GZip
{
public class GZipArchiveFactory : IArchiveFactory
{
/// <inheritdoc/>
public string Name => "GZip";
/// <inheritdoc/>
public IEnumerable<string> GetSupportedExtensions()
{
yield return "gz";
}
/// <inheritdoc/>
public bool IsArchive(Stream stream, string? password = null)
{
return GZipArchive.IsGZipFile(stream);
}
/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(stream, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(fileInfo, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(streams, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(fileInfos, readerOptions);
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.IO;
using SharpCompress.Common;
using SharpCompress.Factories;
using SharpCompress.Readers;
namespace SharpCompress.Archives
@@ -13,33 +14,15 @@ namespace SharpCompress.Archives
/// <remarks>
/// Currently implemented by:<br/>
/// <list type="table">
/// <item><see cref="Tar.TarArchiveFactory"/></item>
/// <item><see cref="Rar.RarArchiveFactory"/></item>
/// <item><see cref="Zip.ZipArchiveFactory"/></item>
/// <item><see cref="GZip.GZipArchiveFactory"/></item>
/// <item><see cref="SevenZip.SevenZipArchiveFactory"/></item>
/// <item><see cref="Factories.TarFactory"/></item>
/// <item><see cref="Factories.RarFactory"/></item>
/// <item><see cref="Factories.ZipFactory"/></item>
/// <item><see cref="Factories.GZipFactory"/></item>
/// <item><see cref="Factories.SevenZipFactory"/></item>
/// </list>
/// </remarks>
public interface IArchiveFactory
public interface IArchiveFactory : IFactory
{
/// <summary>
/// Gets the archive Type name
/// </summary>
string Name { get; }
/// <summary>
/// returns the extensions typically used by this archive type.
/// </summary>
/// <returns></returns>
IEnumerable<string> GetSupportedExtensions();
/// <summary>
/// Returns true if the stream represents an archive of the format defined by this type.
/// </summary>
/// <param name="stream"></param>
/// /// <param name="password">optional password</param>
bool IsArchive(Stream stream, string? password = null);
/// <summary>
/// Opens an Archive for random access.
/// </summary>
@@ -53,19 +36,5 @@ namespace SharpCompress.Archives
/// <param name="fileInfo">the file to open.</param>
/// <param name="readerOptions">reading options.</param>
IArchive Open(System.IO.FileInfo fileInfo, ReaderOptions? readerOptions = null);
/// <summary>
/// Constructor with IEnumerable FileInfo objects, multi and split support.
/// </summary>
/// <param name="streams"></param>
/// <param name="readerOptions">reading options.</param>
IArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null);
/// <summary>
/// Constructor with IEnumerable Stream objects, multi and split support.
/// </summary>
/// <param name="fileInfos"></param>
/// <param name="readerOptions">reading options.</param>
IArchive Open(IEnumerable<System.IO.FileInfo> fileInfos, ReaderOptions? readerOptions = null);
}
}

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.IO;
using SharpCompress.Common;
using SharpCompress.Factories;
using SharpCompress.Readers;
namespace SharpCompress.Archives
{
/// <summary>
/// Represents a factory used to identify and open archives.
/// </summary>
/// <remarks>
/// Implemented by:<br/>
/// <list type="table">
/// <item><see cref="Factories.TarFactory"/></item>
/// <item><see cref="Factories.RarFactory"/></item>
/// <item><see cref="Factories.ZipFactory"/></item>
/// <item><see cref="Factories.GZipFactory"/></item>
/// <item><see cref="Factories.SevenZipFactory"/></item>
/// </list>
/// </remarks>
public interface IMultiArchiveFactory : IFactory
{
/// <summary>
/// Constructor with IEnumerable FileInfo objects, multi and split support.
/// </summary>
/// <param name="streams"></param>
/// <param name="readerOptions">reading options.</param>
IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null);
/// <summary>
/// Constructor with IEnumerable Stream objects, multi and split support.
/// </summary>
/// <param name="fileInfos"></param>
/// <param name="readerOptions">reading options.</param>
IArchive Open(IReadOnlyList<System.IO.FileInfo> fileInfos, ReaderOptions? readerOptions = null);
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharpCompress.Archives
{
/// <summary>
/// Decorator for <see cref="Factories.Factory"/> used to declare an archive format as able to create writeable archives
/// </summary>
/// <remarks>
/// Implemented by:<br/>
/// <list type="table">
/// <item><see cref="Factories.TarFactory"/></item>
/// <item><see cref="Factories.ZipFactory"/></item>
/// <item><see cref="Factories.GZipFactory"/></item>
/// </list>
public interface IWriteableArchiveFactory : Factories.IFactory
{
/// <summary>
/// Creates a new, empty archive, ready to be written.
/// </summary>
/// <returns></returns>
IWritableArchive CreateWriteableArchive();
}
}

View File

@@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Readers;
namespace SharpCompress.Archives.Rar
{
public class RarArchiveFactory : IArchiveFactory
{
/// <inheritdoc/>
public string Name => "Rar";
/// <inheritdoc/>
public IEnumerable<string> GetSupportedExtensions()
{
yield return "rar";
yield return "cbr";
}
/// <inheritdoc/>
public bool IsArchive(Stream stream, string? password = null)
{
return RarArchive.IsRarFile(stream);
}
/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(stream, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(fileInfo, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(streams, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(fileInfos, readerOptions);
}
}
}

View File

@@ -1,51 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Readers;
namespace SharpCompress.Archives.SevenZip
{
public class SevenZipArchiveFactory : IArchiveFactory
{
/// <inheritdoc/>
public string Name => "7Zip";
/// <inheritdoc/>
public IEnumerable<string> GetSupportedExtensions()
{
yield return "7z";
}
/// <inheritdoc/>
public bool IsArchive(Stream stream, string? password = null)
{
return SevenZipArchive.IsSevenZipFile(stream);
}
/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return SevenZipArchive.Open(stream, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return SevenZipArchive.Open(fileInfo, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
return SevenZipArchive.Open(streams, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return SevenZipArchive.Open(fileInfos, readerOptions);
}
}
}

View File

@@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Readers;
namespace SharpCompress.Archives.Tar
{
public class TarArchiveFactory : IArchiveFactory
{
/// <inheritdoc/>
public string Name => "Tar";
/// <inheritdoc/>
public IEnumerable<string> GetSupportedExtensions()
{
yield return "tar";
}
/// <inheritdoc/>
public bool IsArchive(Stream stream, string? password = null)
{
return TarArchive.IsTarFile(stream);
}
/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return TarArchive.Open(stream, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return TarArchive.Open(fileInfo, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
return TarArchive.Open(streams, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return TarArchive.Open(fileInfos, readerOptions);
}
}
}

View File

@@ -1,62 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Readers;
namespace SharpCompress.Archives.Zip
{
public class ZipArchiveFactory : IArchiveFactory
{
/// <inheritdoc/>
public string Name => "Zip";
/// <inheritdoc/>
public IEnumerable<string> GetSupportedExtensions()
{
yield return "zip";
yield return "zipx";
yield return "cbz";
}
/// <inheritdoc/>
public bool IsArchive(Stream stream, string? password = null)
{
long startPosition = stream.Position;
if (ZipArchive.IsZipFile(stream, password)) return true;
stream.Seek(startPosition, SeekOrigin.Begin);
//test the zip (last) file of a multipart zip
if (ZipArchive.IsZipMulti(stream)) return true;
return false;
}
/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return ZipArchive.Open(stream, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return ZipArchive.Open(fileInfo, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IEnumerable<Stream> streams, ReaderOptions? readerOptions = null)
{
return ZipArchive.Open(streams, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IEnumerable<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return ZipArchive.Open(fileInfos, readerOptions);
}
}
}

View File

@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpCompress.Archives;
using SharpCompress.Common;
using SharpCompress.IO;
using SharpCompress.Readers;
namespace SharpCompress.Factories
{
/// <inheritdoc/>
public abstract class Factory : IFactory
{
static Factory()
{
RegisterFactory(new ZipFactory());
RegisterFactory(new RarFactory());
RegisterFactory(new SevenZipFactory());
RegisterFactory(new GZipFactory());
RegisterFactory(new TarFactory());
}
private static readonly HashSet<Factory> _factories = new HashSet<Factory>();
/// <summary>
/// Gets the collection of registered <see cref="IFactory"/>.
/// </summary>
public static IEnumerable<IFactory> Factories => _factories;
/// <summary>
/// Registers an archive factory.
/// </summary>
/// <param name="factory">The factory to register.</param>
/// <exception cref="ArgumentNullException"><paramref name="factory"/> must not be null.</exception>
public static void RegisterFactory(Factory factory)
{
factory.CheckNotNull(nameof(factory));
_factories.Add(factory);
}
/// <inheritdoc/>
public abstract string Name { get; }
/// <inheritdoc/>
public virtual ArchiveType? KnownArchiveType => null;
/// <inheritdoc/>
public abstract IEnumerable<string> GetSupportedExtensions();
/// <inheritdoc/>
public abstract bool IsArchive(Stream stream, string? password = null);
/// <inheritdoc/>
public virtual FileInfo? GetFilePart(int index, FileInfo part1)
{
return null;
}
/// <summary>
/// Tries to open an <see cref="IReader"/> from a <see cref="RewindableStream"/>.
/// </summary>
/// <remarks>
/// This method provides extra insight to support loading compressed TAR files.
/// </remarks>
/// <param name="rewindableStream"></param>
/// <param name="options"></param>
/// <param name="reader"></param>
/// <returns></returns>
internal virtual bool TryOpenReader(RewindableStream rewindableStream, ReaderOptions options, out IReader? reader)
{
reader = null;
if (this is IReaderFactory readerFactory)
{
rewindableStream.Rewind(false);
if (this.IsArchive(rewindableStream, options.Password))
{
rewindableStream.Rewind(true);
reader = readerFactory.OpenReader(rewindableStream, options);
return true;
}
}
return false;
}
}
}

View File

@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using SharpCompress.Archives;
using SharpCompress.Archives.GZip;
using SharpCompress.Archives.Tar;
using SharpCompress.Common;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.GZip;
using SharpCompress.Readers.Tar;
using SharpCompress.Writers;
using SharpCompress.Writers.GZip;
namespace SharpCompress.Factories
{
/// <summary>
/// Represents the foundation factory of GZip archive.
/// </summary>
public class GZipFactory : Factory,
IArchiveFactory,
IMultiArchiveFactory,
IReaderFactory,
IWriterFactory,
IWriteableArchiveFactory
{
#region IFactory
/// <inheritdoc/>
public override string Name => "GZip";
/// <inheritdoc/>
public override ArchiveType? KnownArchiveType => ArchiveType.GZip;
/// <inheritdoc/>
public override IEnumerable<string> GetSupportedExtensions()
{
yield return "gz";
}
/// <inheritdoc/>
public override bool IsArchive(Stream stream, string? password = null)
{
return GZipArchive.IsGZipFile(stream);
}
#endregion
#region IArchiveFactory
/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(stream, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(fileInfo, readerOptions);
}
#endregion
#region IMultiArchiveFactory
/// <inheritdoc/>
public IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(streams, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return GZipArchive.Open(fileInfos, readerOptions);
}
#endregion
#region IReaderFactory
/// <inheritdoc/>
internal override bool TryOpenReader(RewindableStream rewindableStream, ReaderOptions options, out IReader? reader)
{
reader = null;
rewindableStream.Rewind(false);
if (GZipArchive.IsGZipFile(rewindableStream))
{
rewindableStream.Rewind(false);
var testStream = new GZipStream(rewindableStream, CompressionMode.Decompress);
if (TarArchive.IsTarFile(testStream))
{
rewindableStream.Rewind(true);
reader = new TarReader(rewindableStream, options, CompressionType.GZip);
return true;
}
rewindableStream.Rewind(true);
reader = OpenReader(rewindableStream, options);
return true;
}
return false;
}
/// <inheritdoc/>
public IReader OpenReader(Stream stream, ReaderOptions? options)
{
return GZipReader.Open(stream, options);
}
#endregion
#region IWriterFactory
/// <inheritdoc/>
public IWriter Open(Stream stream, WriterOptions writerOptions)
{
if (writerOptions.CompressionType != CompressionType.GZip)
{
throw new InvalidFormatException("GZip archives only support GZip compression type.");
}
return new GZipWriter(stream, new GZipWriterOptions(writerOptions));
}
#endregion
#region IWriteableArchiveFactory
/// <inheritdoc/>
public IWritableArchive CreateWriteableArchive()
{
return GZipArchive.Create();
}
#endregion
}
}

View File

@@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SharpCompress.Factories
{
/// <summary>
/// Represents the foundation of an archive factory.
/// </summary>
/// <remarks>
/// To get extended functionality, this type can be cast to:<br/>
/// <see cref="Archives.IArchiveFactory"/><br/>
/// <see cref="Readers.IReaderFactory"/><br/>
/// <see cref="Writers.IWriterFactory"/><br/>
/// </remarks>
public interface IFactory
{
/// <summary>
/// Gets the archive Type name
/// </summary>
string Name { get; }
/// <summary>
/// Gets the archive Type in case it is a well known archive format.
/// </summary>
Common.ArchiveType? KnownArchiveType { get; }
/// <summary>
/// returns the extensions typically used by this archive type.
/// </summary>
/// <returns></returns>
IEnumerable<string> GetSupportedExtensions();
/// <summary>
/// Returns true if the stream represents an archive of the format defined by this type.
/// </summary>
/// <param name="stream">A stream, pointing to the beginning of the archive.</param>
/// <param name="password">optional password</param>
bool IsArchive(Stream stream, string? password = null);
/// <summary>
/// From a passed in archive (zip, rar, 7z, 001), return all parts.
/// </summary>
/// <param name="part1">Path to the first part.</param>
/// <returns>
/// The path to the requested part,
/// or NULL if the part does not exist.
/// </returns>
FileInfo? GetFilePart(int index, FileInfo part1);
}
}

View File

@@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Archives;
using SharpCompress.Archives.Rar;
using SharpCompress.Common;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Rar;
namespace SharpCompress.Factories
{
/// <summary>
/// Represents the foundation factory of RAR archive.
/// </summary>
public class RarFactory : Factory,
IArchiveFactory,
IMultiArchiveFactory,
IReaderFactory
{
#region IArchive
/// <inheritdoc/>
public override string Name => "Rar";
/// <inheritdoc/>
public override ArchiveType? KnownArchiveType => ArchiveType.Rar;
/// <inheritdoc/>
public override IEnumerable<string> GetSupportedExtensions()
{
yield return "rar";
yield return "cbr";
}
/// <inheritdoc/>
public override bool IsArchive(Stream stream, string? password = null)
{
return RarArchive.IsRarFile(stream);
}
/// <inheritdoc/>
public override FileInfo? GetFilePart(int index, FileInfo part1)
{
return RarArchiveVolumeFactory.GetFilePart(index, part1);
}
#endregion
#region IArchiveFactory
/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(stream, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(fileInfo, readerOptions);
}
#endregion
#region IMultiArchiveFactory
/// <inheritdoc/>
public IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(streams, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return RarArchive.Open(fileInfos, readerOptions);
}
#endregion
#region IReaderFactory
/// <inheritdoc/>
public IReader OpenReader(Stream stream, ReaderOptions? options)
{
return RarReader.Open(stream, options);
}
#endregion
}
}

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Archives;
using SharpCompress.Archives.SevenZip;
using SharpCompress.Common;
using SharpCompress.Common.SevenZip;
using SharpCompress.IO;
using SharpCompress.Readers;
namespace SharpCompress.Factories
{
/// <summary>
/// Represents the foundation factory of 7Zip archive.
/// </summary>
public class SevenZipFactory : Factory,
IArchiveFactory,
IMultiArchiveFactory
{
#region IFactory
/// <inheritdoc/>
public override string Name => "7Zip";
/// <inheritdoc/>
public override ArchiveType? KnownArchiveType => ArchiveType.SevenZip;
/// <inheritdoc/>
public override IEnumerable<string> GetSupportedExtensions()
{
yield return "7z";
}
/// <inheritdoc/>
public override bool IsArchive(Stream stream, string? password = null)
{
return SevenZipArchive.IsSevenZipFile(stream);
}
#endregion
#region IArchiveFactory
/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return SevenZipArchive.Open(stream, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return SevenZipArchive.Open(fileInfo, readerOptions);
}
#endregion
#region IMultiArchiveFactory
/// <inheritdoc/>
public IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null)
{
return SevenZipArchive.Open(streams, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return SevenZipArchive.Open(fileInfos, readerOptions);
}
#endregion
#region reader
internal override bool TryOpenReader(RewindableStream rewindableStream, ReaderOptions options, out IReader? reader)
{
reader = null;
return false;
}
#endregion
}
}

View File

@@ -0,0 +1,197 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Archives;
using SharpCompress.Archives.Tar;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Compressors.Xz;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Tar;
using SharpCompress.Writers;
using SharpCompress.Writers.Tar;
namespace SharpCompress.Factories
{
/// <summary>
/// Represents the foundation factory of TAR archive.
/// </summary>
public class TarFactory : Factory,
IArchiveFactory,
IMultiArchiveFactory,
IReaderFactory,
IWriterFactory,
IWriteableArchiveFactory
{
#region IFactory
/// <inheritdoc/>
public override string Name => "Tar";
/// <inheritdoc/>
public override ArchiveType? KnownArchiveType => ArchiveType.Tar;
/// <inheritdoc/>
public override IEnumerable<string> GetSupportedExtensions()
{
// from https://en.wikipedia.org/wiki/Tar_(computing)#Suffixes_for_compressed_files
yield return "tar";
// gzip
yield return "taz";
yield return "tgz";
// bzip2
yield return "tb2";
yield return "tbz";
yield return "tbz2";
yield return "tz2";
// lzma
// yield return "tlz"; // unsupported
// xz
// yield return "txz"; // unsupported
// compress
yield return "tZ";
yield return "taZ";
// zstd
// yield return "tzst"; // unsupported
}
/// <inheritdoc/>
public override bool IsArchive(Stream stream, string? password = null)
{
return TarArchive.IsTarFile(stream);
}
#endregion
#region IArchiveFactory
/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return TarArchive.Open(stream, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return TarArchive.Open(fileInfo, readerOptions);
}
#endregion
#region IMultiArchiveFactory
/// <inheritdoc/>
public IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null)
{
return TarArchive.Open(streams, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return TarArchive.Open(fileInfos, readerOptions);
}
#endregion
#region IReaderFactory
/// <inheritdoc/>
internal override bool TryOpenReader(RewindableStream rewindableStream, ReaderOptions options, out IReader? reader)
{
reader = null;
rewindableStream.Rewind(false);
if (TarArchive.IsTarFile(rewindableStream))
{
rewindableStream.Rewind(true);
reader = OpenReader(rewindableStream, options);
return true;
}
rewindableStream.Rewind(false);
if (BZip2Stream.IsBZip2(rewindableStream))
{
rewindableStream.Rewind(false);
var testStream = new BZip2Stream(NonDisposingStream.Create(rewindableStream), CompressionMode.Decompress, false);
if (TarArchive.IsTarFile(testStream))
{
rewindableStream.Rewind(true);
reader = new TarReader(rewindableStream, options, CompressionType.BZip2);
return true;
}
}
rewindableStream.Rewind(false);
if (LZipStream.IsLZipFile(rewindableStream))
{
rewindableStream.Rewind(false);
var testStream = new LZipStream(NonDisposingStream.Create(rewindableStream), CompressionMode.Decompress);
if (TarArchive.IsTarFile(testStream))
{
rewindableStream.Rewind(true);
reader = new TarReader(rewindableStream, options, CompressionType.LZip);
return true;
}
}
rewindableStream.Rewind(false);
if (XZStream.IsXZStream(rewindableStream))
{
rewindableStream.Rewind(true);
var testStream = new XZStream(rewindableStream);
if (TarArchive.IsTarFile(testStream))
{
rewindableStream.Rewind(true);
reader = new TarReader(rewindableStream, options, CompressionType.Xz);
return true;
}
}
return false;
}
/// <inheritdoc/>
public IReader OpenReader(Stream stream, ReaderOptions? options)
{
return TarReader.Open(stream, options);
}
#endregion
#region IWriterFactory
/// <inheritdoc/>
public IWriter Open(Stream stream, WriterOptions writerOptions)
{
return new TarWriter(stream, new TarWriterOptions(writerOptions));
}
#endregion
#region IWriteableArchiveFactory
/// <inheritdoc/>
public IWritableArchive CreateWriteableArchive()
{
return TarArchive.Create();
}
#endregion
}
}

View File

@@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SharpCompress.Archives;
using SharpCompress.Archives.Zip;
using SharpCompress.Common;
using SharpCompress.Common.Tar.Headers;
using SharpCompress.IO;
using SharpCompress.Readers;
using SharpCompress.Readers.Zip;
using SharpCompress.Writers;
using SharpCompress.Writers.Zip;
namespace SharpCompress.Factories
{
/// <summary>
/// Represents the foundation factory of ZIP archive.
/// </summary>
public class ZipFactory : Factory,
IArchiveFactory,
IMultiArchiveFactory,
IReaderFactory,
IWriterFactory,
IWriteableArchiveFactory
{
#region IFactory
/// <inheritdoc/>
public override string Name => "Zip";
/// <inheritdoc/>
public override ArchiveType? KnownArchiveType => ArchiveType.Zip;
/// <inheritdoc/>
public override IEnumerable<string> GetSupportedExtensions()
{
yield return "zip";
yield return "zipx";
yield return "cbz";
}
/// <inheritdoc/>
public override bool IsArchive(Stream stream, string? password = null)
{
var startPosition = stream.CanSeek
? stream.Position
: -1;
// probe for single volume zip
if (ZipArchive.IsZipFile(stream, password))
return true;
// probe for a multipart zip
if (!stream.CanSeek)
return false;
stream.Position = startPosition;
//test the zip (last) file of a multipart zip
if (ZipArchive.IsZipMulti(stream))
return true;
return false;
}
/// <inheritdoc/>
public override FileInfo? GetFilePart(int index, FileInfo part1)
{
return ZipArchiveVolumeFactory.GetFilePart(index, part1);
}
#endregion
#region IArchiveFactory
/// <inheritdoc/>
public IArchive Open(Stream stream, ReaderOptions? readerOptions = null)
{
return ZipArchive.Open(stream, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(FileInfo fileInfo, ReaderOptions? readerOptions = null)
{
return ZipArchive.Open(fileInfo, readerOptions);
}
#endregion
#region IMultiArchiveFactory
/// <inheritdoc/>
public IArchive Open(IReadOnlyList<Stream> streams, ReaderOptions? readerOptions = null)
{
return ZipArchive.Open(streams, readerOptions);
}
/// <inheritdoc/>
public IArchive Open(IReadOnlyList<FileInfo> fileInfos, ReaderOptions? readerOptions = null)
{
return ZipArchive.Open(fileInfos, readerOptions);
}
#endregion
#region IReaderFactory
/// <inheritdoc/>
public IReader OpenReader(Stream stream, ReaderOptions? options)
{
return ZipReader.Open(stream, options);
}
#endregion
#region IWriterFactory
/// <inheritdoc/>
public IWriter Open(Stream stream, WriterOptions writerOptions)
{
return new ZipWriter(stream, new ZipWriterOptions(writerOptions));
}
#endregion
#region IWriteableArchiveFactory
/// <inheritdoc/>
public IWritableArchive CreateWriteableArchive()
{
return ZipArchive.Create();
}
#endregion
}
}

View File

@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpCompress.IO;
namespace SharpCompress.Readers
{
public interface IReaderFactory : Factories.IFactory
{
/// <summary>
/// Opens a Reader for Non-seeking usage
/// </summary>
/// <param name="stream"></param>
/// <param name="options"></param>
/// <returns></returns>
IReader OpenReader(Stream stream, ReaderOptions? options);
}
}

View File

@@ -1,20 +1,9 @@
using System;
using System.IO;
using SharpCompress.Archives.GZip;
using SharpCompress.Archives.Rar;
using SharpCompress.Archives.Tar;
using SharpCompress.Archives.Zip;
using System.Linq;
using SharpCompress.Common;
using SharpCompress.Compressors;
using SharpCompress.Compressors.BZip2;
using SharpCompress.Compressors.Deflate;
using SharpCompress.IO;
using SharpCompress.Readers.GZip;
using SharpCompress.Readers.Rar;
using SharpCompress.Readers.Tar;
using SharpCompress.Readers.Zip;
using SharpCompress.Compressors.LZMA;
using SharpCompress.Compressors.Xz;
namespace SharpCompress.Readers
{
@@ -33,74 +22,15 @@ namespace SharpCompress.Readers
{
LeaveStreamOpen = false
};
RewindableStream rewindableStream = new RewindableStream(stream);
rewindableStream.StartRecording();
if (ZipArchive.IsZipFile(rewindableStream, options.Password))
{
rewindableStream.Rewind(true);
return ZipReader.Open(rewindableStream, options);
}
rewindableStream.Rewind(false);
if (GZipArchive.IsGZipFile(rewindableStream))
{
rewindableStream.Rewind(false);
GZipStream testStream = new GZipStream(rewindableStream, CompressionMode.Decompress);
if (TarArchive.IsTarFile(testStream))
{
rewindableStream.Rewind(true);
return new TarReader(rewindableStream, options, CompressionType.GZip);
}
rewindableStream.Rewind(true);
return GZipReader.Open(rewindableStream, options);
}
rewindableStream.Rewind(false);
if (BZip2Stream.IsBZip2(rewindableStream))
foreach(var factory in Factories.Factory.Factories.OfType<Factories.Factory>())
{
rewindableStream.Rewind(false);
BZip2Stream testStream = new BZip2Stream(NonDisposingStream.Create(rewindableStream), CompressionMode.Decompress, false);
if (TarArchive.IsTarFile(testStream))
{
rewindableStream.Rewind(true);
return new TarReader(rewindableStream, options, CompressionType.BZip2);
}
}
if (factory.TryOpenReader(rewindableStream, options, out var reader) && reader != null) return reader;
}
rewindableStream.Rewind(false);
if (LZipStream.IsLZipFile(rewindableStream))
{
rewindableStream.Rewind(false);
LZipStream testStream = new LZipStream(NonDisposingStream.Create(rewindableStream), CompressionMode.Decompress);
if (TarArchive.IsTarFile(testStream))
{
rewindableStream.Rewind(true);
return new TarReader(rewindableStream, options, CompressionType.LZip);
}
}
rewindableStream.Rewind(false);
if (RarArchive.IsRarFile(rewindableStream, options))
{
rewindableStream.Rewind(true);
return RarReader.Open(rewindableStream, options);
}
rewindableStream.Rewind(false);
if (TarArchive.IsTarFile(rewindableStream))
{
rewindableStream.Rewind(true);
return TarReader.Open(rewindableStream, options);
}
rewindableStream.Rewind(false);
if (XZStream.IsXZStream(rewindableStream))
{
rewindableStream.Rewind(true);
XZStream testStream = new XZStream(rewindableStream);
if (TarArchive.IsTarFile(testStream))
{
rewindableStream.Rewind(true);
return new TarReader(rewindableStream, options, CompressionType.Xz);
}
}
throw new InvalidOperationException("Cannot determine compressed stream type. Supported Reader Formats: Zip, GZip, BZip2, Tar, Rar, LZip, XZ");
}
}

View File

@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using SharpCompress.Factories;
namespace SharpCompress.Writers
{
public interface IWriterFactory : IFactory
{
IWriter Open(Stream stream, WriterOptions writerOptions);
}
}

View File

@@ -1,9 +1,8 @@
using System;
using System;
using System.IO;
using System.Linq;
using SharpCompress.Common;
using SharpCompress.Writers.GZip;
using SharpCompress.Writers.Tar;
using SharpCompress.Writers.Zip;
namespace SharpCompress.Writers
{
@@ -11,29 +10,15 @@ namespace SharpCompress.Writers
{
public static IWriter Open(Stream stream, ArchiveType archiveType, WriterOptions writerOptions)
{
switch (archiveType)
{
case ArchiveType.GZip:
{
if (writerOptions.CompressionType != CompressionType.GZip)
{
throw new InvalidFormatException("GZip archives only support GZip compression type.");
}
return new GZipWriter(stream, new GZipWriterOptions(writerOptions));
}
case ArchiveType.Zip:
{
return new ZipWriter(stream, new ZipWriterOptions(writerOptions));
}
case ArchiveType.Tar:
{
return new TarWriter(stream, new TarWriterOptions(writerOptions));
}
default:
{
throw new NotSupportedException("Archive Type does not have a Writer: " + archiveType);
}
}
var factory = Factories.Factory
.Factories
.OfType<IWriterFactory>()
.FirstOrDefault(item => item.KnownArchiveType == archiveType);
if (factory != null)
return factory.Open(stream, writerOptions);
throw new NotSupportedException("Archive Type does not have a Writer: " + archiveType);
}
}
}
}