Files
sharpcompress/docs/API.md
2026-10-02 08:59:29 +01:00

34 KiB

API Quick Reference

Quick reference for commonly used SharpCompress APIs.

Instance Services

The recommended entry point is SharpCompressClient in the SharpCompress namespace. Its constructor accepts an optional immutable SharpCompressConfiguration containing Formats and Providers. The client implements ISharpCompressClient and exposes independently injectable workflow services:

API Purpose
client.OpenArchive(...) / OpenAsyncArchive(...) Random access to seekable archives
client.OpenReader(...) / OpenAsyncReader(...) Forward-only reading, including non-seekable streams
client.OpenWriter(..., ArchiveType, IWriterOptions) / OpenAsyncWriter(...) Streaming archive writing
client.CreateArchive<TOptions>() Create a writable archive for the options type
client.Inspector (IArchiveInspector) Detection, inspection, and multipart path discovery
client.Extractor (IArchiveExtractor) Directory, file, and stream extraction
client.FileWriter (IArchiveFileWriter) Filesystem source writing and writable-archive saving

Open methods accept paths, FileInfo, or streams. Archive opening also accepts ordered lists of files or streams for multipart archives. Async methods accept CancellationToken. The caller disposes returned archives, readers, and writers; the reusable client needs no disposal. Client services may be shared concurrently when custom factories and providers are thread-safe. Individual opened archives/readers/writers remain operation-local.

using SharpCompress;
using SharpCompress.Archives;
using SharpCompress.Common;
using SharpCompress.Writers;

var client = new SharpCompressClient();
using var archive = client.OpenArchive("archive.zip");
client.Extractor.ExtractToDirectory(archive, "output", new ExtractionOptions());

var detection = client.Inspector.DetectArchive("archive.tar.gz");
var information = await client.Inspector.InspectArchiveAsync(
    "archive.zip", cancellationToken: cancellationToken);

await using var writer = await client.OpenAsyncWriter(
    "output.zip", ArchiveType.Zip, new WriterOptions(CompressionType.Deflate),
    cancellationToken);
await client.FileWriter.WriteDirectoryAsync(
    writer, "input", searchOption: SearchOption.AllDirectories,
    cancellationToken: cancellationToken);

IArchiveInspector, IArchiveExtractor, and IArchiveFileWriter are in SharpCompress.Archives. ArchiveExtractor is a public stateless implementation, and ArchiveFileWriter can be constructed with a SharpCompressConfiguration or defaults. Applications can constructor-inject these interfaces separately, or use the client properties. No public API requires Pure.DI.

Extraction Service

  • ExtractToDirectory(IArchive, ...) and ExtractToDirectoryAsync(IAsyncArchive, ...) extract all entries, handling solid archives sequentially. They accept extraction options and an optional archive-level progress reporter.
  • ExtractToDirectory(IReader, ...) and its IAsyncReader equivalent advance to and extract every remaining unread entry.
  • ExtractEntryToDirectory(IReader, ...) and its async equivalent extract only the current entry.
  • Entry overloads of ExtractToDirectory, ExtractToFile, and ExtractToStream operate on IArchiveEntry; reader overloads of ExtractToFile and ExtractToStream operate on the current reader entry.
  • Extraction leaves supplied archives/readers/destination streams open. Directory extraction requires an existing destination and retains path/link protection, overwrite and metadata options. File/directory extraction validates checksums by default. Archive-entry stream extraction validates optional checksums when an ExtractionOptions value is supplied, matching entry.WriteTo(...) behavior.

Filesystem Writing Service

WriteFile and WriteDirectory accept IWriter; their async equivalents accept IAsyncWriter. Directory writing accepts a search pattern, optional Func<string, bool> filter, and SearchOption. Entry paths are relative to the source directory and use / separators. AddFile and AddDirectory operate on writable archives, transferring accepted file-stream ownership to the archive. Async versions accept cancellation tokens. SaveToFile<TOptions> and SaveToFileAsync<TOptions> save to a FileInfo using the client's default providers unless the supplied options explicitly override them.

Format Configuration

FormatRegistry is in SharpCompress.Factories. It copies its input collections and exposes read-only Factories and TarWrappers lists. Default contains built-in factories in recognition order; Empty contains neither factories nor wrappers. A constructor supplied only factories uses the default wrappers. Use WithTarWrappers(...) to select wrappers explicitly.

With(factory, prepend: false) creates a copy. A matching known archive type is replaced in place; custom formats with no known type match by ordinal name. New formats append by default; prepend: true places a new format first. Factory ordering is explicit and consistent across opening and inspection. WithTarWrappers(...) also recomputes the required rewind-buffer size. Factories can implement IFactory and capability interfaces directly without deriving from Factory.

Provider precedence is explicit operation options → client configuration → built-in default. Explicitly assigning CompressionProviderRegistry.Default overrides a custom client default. Client operations copy built-in reader/writer options before resolving dependencies, preserving caller-owned settings and format-specific fields. Custom IWriterOptions implementations retain their own provider settings and are passed to their custom factory unchanged.

Migrating to Instance Services

Existing usage Instance-based usage
ArchiveFactory.OpenArchive(...) client.OpenArchive(...)
ReaderFactory.OpenReader(...) client.OpenReader(...)
WriterFactory.OpenWriter(...) client.OpenWriter(...)
ArchiveFactory.DetectArchive(...) / InspectArchive(...) client.Inspector.DetectArchive(...) / InspectArchive(...)
archive.WriteToDirectory(...) client.Extractor.ExtractToDirectory(archive, ...)
reader.WriteAllToDirectory(...) client.Extractor.ExtractToDirectory(reader, ...)
reader.WriteEntryToDirectory(...) client.Extractor.ExtractEntryToDirectory(reader, ...)
entry.WriteTo(...) client.Extractor.ExtractToStream(entry, ...)
writer.WriteAll(...) client.FileWriter.WriteDirectory(writer, ...)
archive.AddAllFromDirectory(...) client.FileWriter.AddDirectory(archive, ...)
archive.SaveTo(filePath, options) client.FileWriter.SaveToFile(archive, new FileInfo(filePath), options)
Removed Factory.RegisterFactory(factory) new SharpCompressClient(new SharpCompressConfiguration(FormatRegistry.Default.With(factory)))
Removed Factory.Factories client.Configuration.Formats.Factories on the concrete client

Async usages follow the equivalent async service methods. Static convenience factories and workflow extensions delegate to the immutable default client's instance services. Their configuration is independent of custom clients. TarWrapper.Wrappers now exposes IReadOnlyList<TarWrapper> instead of a mutable array; use FormatRegistry.WithTarWrappers(...) for customization.

Factory Methods

Opening Archives

// Auto-detect format
using (var reader = ReaderFactory.OpenReader(stream))
{
    // Works with Zip, Tar, GZip, Rar, etc. Reading 7Zip needs the Archive API
}

// Specific format - Archive API
using (var archive = ZipArchive.OpenArchive("file.zip"))
using (var archive = TarArchive.OpenArchive("file.tar"))
using (var archive = RarArchive.OpenArchive("file.rar"))
using (var archive = SevenZipArchive.OpenArchive("file.7z"))
using (var archive = GZipArchive.OpenArchive("file.gz"))

// With fluent options (preferred)
var options = ReaderOptions.ForEncryptedArchive("password")
    .WithArchiveEncoding(new ArchiveEncoding { Default = Encoding.GetEncoding(932) });
using (var archive = ZipArchive.OpenArchive("encrypted.zip", options))

// Alternative: object initializer
var options2 = new ReaderOptions
{
    Password = "password",
    LeaveStreamOpen = true,
    ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
};
// Auto-detect and open with the Archive API
using (var archive = ArchiveFactory.OpenArchive("archive.zip"))
{
    foreach (var entry in archive.Entries)
    {
        Console.WriteLine(entry.Key);
    }
}

// Detect before opening
if (ArchiveFactory.IsArchive("archive.zip", out var archiveType))
{
    Console.WriteLine($"Detected {archiveType}");
}

// Detect the format and choose an API without enumerating entries.
var detection = ArchiveFactory.DetectArchive("archive.tar.xz");
if (detection is not null)
{
    Console.WriteLine($"Container: {detection.ContainerType}");
    Console.WriteLine($"Outer compression: {detection.OuterCompressionType}");
    Console.WriteLine($"Supported APIs: {detection.SupportedApis}");
}

// InspectArchive parses complete archive metadata. It can be expensive for
// sequential formats such as TAR and compressed TAR.
var information = ArchiveFactory.InspectArchive("archive.zip");
if (information is not null)
{
    Console.WriteLine($"Entries: {information.EntryCount}");
    Console.WriteLine($"Solid streams: {information.SolidStreamCount}");
    Console.WriteLine($"Some ZIP entry sizes arrive after their data: {information.Zip?.HasEntriesWithDeferredSizes}");
}

var asyncInformation = await ArchiveFactory.InspectArchiveAsync("archive.zip", cancellationToken);

// Multi-volume archives
var parts = ArchiveFactory.GetFileParts("archive.part1.rar")
    .Select(path => new FileInfo(path))
    .ToArray();
using (var archive = ArchiveFactory.OpenArchive(parts))
{
    archive.WriteToDirectory(@"C:\output");
}

DetectArchive identifies the logical container, its outer compression wrapper, and whether the Archive and Reader APIs are available without enumerating entries. Compressed TAR wrappers such as .tar.gz and .tar.xz are identified as ArchiveType.Tar with an outer compression type and ArchiveAccessMode.Reader.

InspectArchive enumerates metadata and returns ArchiveInformation. It reports Partial status for missing volumes or encrypted headers without a password; malformed archives and incorrect passwords throw. ZIP-specific metadata is exposed through ArchiveInformation.Zip.

ArchiveInformation.Detection contains the format identification collected during inspection, so there is no need to call DetectArchive first when requesting metadata. Inspection reuses the selected factory and compression wrapper to open the source.

Status.Complete means no known inspection limitations apply; it does not guarantee that every nullable metadata property has a value. EntriesWithUnknownSizeCount counts entries whose uncompressed sizes a forward-only reader cannot know before reading their data. For example, ZIP inspection can obtain final sizes from the central directory while still reporting entries with deferred local-header sizes.

Detection and inspection result types are in the SharpCompress.Detection namespace.

The stream overloads of DetectArchive and InspectArchive preserve the supplied stream's position and leave it open, including when ReaderOptions.LeaveStreamOpen is false.

Migrating from GetArchiveInformation

Removed API Replacement
GetArchiveInformation(...) used only to identify a format DetectArchive(...)
GetArchiveInformation(...) used to enumerate archive details InspectArchive(...)
GetArchiveInformationAsync(...) DetectArchiveAsync(...) or InspectArchiveAsync(...)
Type Detection.ContainerType
SupportsRandomAccess Detection.SupportedApis.HasFlag(ArchiveAccessMode.Archive)
ZipDataDescriptorEntryCount Zip.HasEntriesWithDeferredSizes
SolidStreamCount SolidStreamCount

Creating Archives with the Writer API

// Writer Factory
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate))
{
    // Write entries
}

// Specific writer
using (var archive = ZipArchive.CreateArchive())
using (var archive = TarArchive.CreateArchive())
using (var archive = GZipArchive.CreateArchive())

// With fluent options (preferred)
var options = WriterOptions.ForZip()
    .WithCompressionLevel(9)
    .WithLeaveStreamOpen(false)
    .WithBufferSize(131072);
using (var archive = ZipArchive.CreateArchive())
{
    archive.SaveTo("output.zip", options);
}

// Alternative: constructor with object initializer
var options2 = new WriterOptions(CompressionType.Deflate)
{
    CompressionLevel = 9,
    LeaveStreamOpen = false,
    BufferSize = 131072
};

WriterOptions.BufferSize controls stream copy buffers used while writing archive entries. If it is not set, SharpCompress falls back to Constants.BufferSize.


Archive API Methods

Reading/Extracting

using (var archive = ZipArchive.OpenArchive("file.zip"))
{
    // Get all entries
    IEnumerable<IArchiveEntry> entries = archive.Entries;

    // Find specific entry
    var entry = archive.Entries.FirstOrDefault(e => e.Key == "file.txt");

    // Extract all
    archive.WriteToDirectory(@"C:\output");

    // Extract single entry
    var firstEntry = archive.Entries.First();
    firstEntry.WriteToFile(@"C:\output\file.txt");

    // Extract single entry to a stream with extraction options
    using (var outputStream = File.Create(@"C:\output\file.txt"))
    {
        firstEntry.WriteTo(
            outputStream,
            new ExtractionOptions { CheckCrc = false }
        );
    }

    // Get entry stream
    using (var stream = entry.OpenEntryStream())
    {
        stream.CopyTo(outputStream);
    }
}

// Async extraction (requires IAsyncArchive)
await using (var asyncArchive = await ZipArchive.OpenAsyncArchive("file.zip"))
{
    // Extract all entries asynchronously
    await asyncArchive.WriteToDirectoryAsync(
        @"C:\output",
        cancellationToken: cancellationToken
    );
}

// Open a specific entry stream asynchronously
await using (var asyncArchive = await ZipArchive.OpenAsyncArchive("file.zip"))
{
    await foreach (var entry in asyncArchive.EntriesAsync)
    {
        await using (var outputStream = File.Create(@"C:\output\" + entry.Key))
        {
            await entry.WriteToAsync(
                outputStream,
                new ExtractionOptions { CheckCrc = false },
                cancellationToken: cancellationToken
            );
        }

        using (var stream = await entry.OpenEntryStreamAsync(cancellationToken))
        {
            // ...
        }
    }
}

Entry Properties

foreach (var entry in archive.Entries)
{
    string name = entry.Key;              // Entry name/path
    long size = entry.Size;               // Uncompressed size
    long compressedSize = entry.CompressedSize;
    bool isDir = entry.IsDirectory;
    DateTime? modTime = entry.LastModifiedTime;
    CompressionType compression = entry.CompressionType;
}

Creating Archives with the Archive API

using (var archive = ZipArchive.CreateArchive())
{
    // Add file
    archive.AddEntry("file.txt", @"C:\source\file.txt");

    // Add multiple files
    archive.AddAllFromDirectory(@"C:\source");
    archive.AddAllFromDirectory(@"C:\source", "*.txt");  // Pattern

    // Save to file
    archive.SaveTo("output.zip", CompressionType.Deflate);

    // Save to stream
    archive.SaveTo(outputStream, new WriterOptions(CompressionType.Deflate)
    {
        CompressionLevel = 9,
        LeaveStreamOpen = true
    });
}

Reader API Methods

Forward-Only Reading

using (var stream = File.OpenRead("file.zip"))
using (var reader = ReaderFactory.OpenReader(stream))
{
    while (reader.MoveToNextEntry())
    {
        IEntry entry = reader.Entry;

        if (!entry.IsDirectory)
        {
            // Extract entry
            reader.WriteEntryToDirectory(@"C:\output");
            reader.WriteEntryToFile(@"C:\output\file.txt");

            // Or get stream
            using (var entryStream = reader.OpenEntryStream())
            {
                entryStream.CopyTo(outputStream);
            }
        }
    }
}

// Async variants (use OpenAsyncReader to get IAsyncReader)
using (var stream = File.OpenRead("file.zip"))
await using (var reader = await ReaderFactory.OpenAsyncReader(stream))
{
    while (await reader.MoveToNextEntryAsync())
    {
        await reader.WriteEntryToFileAsync(
            @"C:\output\" + reader.Entry.Key,
            cancellationToken: cancellationToken
        );
    }

    // Async extraction of all entries
    await reader.WriteAllToDirectoryAsync(
        @"C:\output",
        cancellationToken: cancellationToken
    );
}

Writer API Methods

Creating Archives (Streaming)

using (var stream = File.Create("output.zip"))
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate))
{
    // Write single file
    using (var fileStream = File.OpenRead("source.txt"))
    {
        writer.Write("entry.txt", fileStream, DateTime.Now);
    }
    
    // Write directory
    writer.WriteAll("C:\\source", "*", SearchOption.AllDirectories);
    writer.WriteAll("C:\\source", "*.txt", SearchOption.TopDirectoryOnly);
}

// Async variants: use OpenAsyncWriter to get IAsyncWriter
await using var stream = File.Create("output.zip");
await using var writer = await WriterFactory.OpenAsyncWriter(stream, ArchiveType.Zip, new WriterOptions(CompressionType.Deflate), cancellationToken);

using (var fileStream = File.OpenRead("source.txt"))
{
    await writer.WriteAsync("entry.txt", fileStream, DateTime.Now, cancellationToken);
}

await writer.WriteAllAsync("C:\\source", "*", SearchOption.AllDirectories, cancellationToken);

Common Options

ReaderOptions

Use preset properties and fluent helpers for common configurations:

// External stream with password and custom encoding
var options = ReaderOptions.ForExternalStream
    .WithPassword("password")
    .WithArchiveEncoding(new ArchiveEncoding { Default = Encoding.GetEncoding(932) });

using (var archive = ZipArchive.OpenArchive("file.zip", options))
{
    // ...
}

// Open-time presets
var external = ReaderOptions.ForExternalStream;
var owned = ReaderOptions.ForFilePath;
var encrypted = ReaderOptions.ForEncryptedArchive("password");
var encoded = ReaderOptions.ForEncoding(new ArchiveEncoding { Default = Encoding.UTF8 });
var sfx = ReaderOptions.ForSelfExtractingArchive("password");

// Faster detection when the container is known
var hinted = ReaderOptions.ForExternalStream.WithExtensionHint("tar.gz");

// Increase for non-seekable streams with large detection probes, such as SFX RAR
var buffered = ReaderOptions.ForExternalStream.WithRewindableBufferSize(1_048_576);

// Opt in to formats' optional parallel decode (e.g. 7-Zip's automatic parallel LZMA2
// solid-folder decode); default is sequential decoding
var parallel = ReaderOptions.ForExternalStream.WithEnableParallelism(true);

// Extraction presets
var safeOptions = ExtractionOptions.SafeExtract;  // No overwrite
var flatOptions = ExtractionOptions.FlatExtract;  // No directory structure
var metadataOptions = ExtractionOptions.PreserveMetadata; // Keep timestamps and attributes

// Tune extraction copy buffering
var extractionOptions = new ExtractionOptions
{
    BufferSize = 131072,
    CheckCrc = true, // Default: validate entry checksums when archive metadata provides one
};

// Factory defaults:
// - file path / FileInfo overloads use LeaveStreamOpen = false
// - stream overloads use LeaveStreamOpen = true

Alternative: traditional object initializer:

var options = new ReaderOptions
{
    Password = "password",
    LeaveStreamOpen = true,
    ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) },
    ExtensionHint = "zip",
    LookForHeader = true,
    DisableCheckIncomplete = false,
    BufferSize = 81920,
    RewindableBufferSize = 1_048_576,
    EnableParallelism = false,
};

var extractionOptions = new ExtractionOptions
{
    ExtractFullPath = true,
    Overwrite = true,
    BufferSize = 131072,
};

WriterOptions

Factory methods provide a clean, discoverable way to create writer options:

// Factory methods for common archive types
var zipOptions = WriterOptions.ForZip()                           // ZIP with Deflate
    .WithCompressionLevel(9)                                      // 0-9 for Deflate
    .WithLeaveStreamOpen(false);                                  // Close stream when done

var tarOptions = WriterOptions.ForTar(CompressionType.GZip)       // TAR with GZip
    .WithLeaveStreamOpen(false);

var gzipOptions = WriterOptions.ForGZip()                         // GZip file
    .WithCompressionLevel(6);

archive.SaveTo("output.zip", zipOptions);

Use typed writer options when a format exposes extra settings:

// ZIP archive-level options
var zipWriterOptions = new ZipWriterOptions(CompressionType.Deflate)
{
    ArchiveComment = "Created by SharpCompress",
    UseZip64 = true,
    CompressionLevel = 9,
};

// ZIP per-entry options
using (var writer = new ZipWriter(outputStream, zipWriterOptions))
using (var source = File.OpenRead("source.txt"))
{
    writer.Write("entry.txt", source, new ZipWriterEntryOptions
    {
        CompressionType = CompressionType.ZStandard,
        CompressionLevel = 3,
        EntryComment = "per-entry comment",
        ModificationDateTime = DateTime.UtcNow,
        EnableZip64 = true,
    });
}

// TAR-specific options
var tarOptions = new TarWriterOptions(CompressionType.GZip, finalizeArchiveOnClose: true)
{
    HeaderFormat = TarHeaderWriteFormat.GNU_TAR_LONG_LINK,
};

// GZip-specific options
var gzipOptions = new GZipWriterOptions(compressionLevel: 9);

// 7z writing requires a seekable output stream and writes non-solid archives
var sevenZipOptions = new SevenZipWriterOptions(CompressionType.LZMA2)
{
    CompressHeader = true,
    LzmaProperties = new LzmaEncoderProperties(),
};

Alternative: traditional constructor with object initializer:

var options = new WriterOptions(CompressionType.Deflate)
{
    CompressionLevel = 9,
    LeaveStreamOpen = true,
};
archive.SaveTo("output.zip", options);

WriterOptions.EnableParallelism (also available on ZipWriterOptions, TarWriterOptions, GZipWriterOptions, and SevenZipWriterOptions) opts in to a format's optional parallel encode. No writer currently implements parallel encoding, so this is a no-op today; it is reserved for future use and mirrors ReaderOptions.EnableParallelism.

Extraction behavior

var options = new ExtractionOptions
{
    ExtractFullPath = true,                         // Recreate directory structure
    Overwrite = true,                               // Overwrite existing files
    PreserveFileTime = true,                        // Keep original timestamps
    CheckCrc = true                                 // Validate payload checksums when available
};

using (var archive = ZipArchive.OpenArchive("file.zip"))
{
    archive.WriteToDirectory(@"C:\output", options);
}

CheckCrc validates archive-level payload checksums when the format stores reliable metadata, such as ZIP CRC32 values. Formats without payload checksums skip this validation. Decompressor integrity checks that are required to decode a stream may still fail even when CheckCrc is disabled.

When using SymbolicLinkHandler, directory extraction rejects link targets outside the extraction root and never follows symbolic links or reparse points while extracting later entries. The handler itself remains trusted application code.

Options matrix

ReaderOptions: open-time behavior (password, encoding, stream ownership)
ExtractionOptions: extract-time behavior (overwrite, paths, timestamps, attributes, symlinks)
WriterOptions: write-time behavior (compression type/level, encoding, stream ownership)
ZipWriterEntryOptions: per-entry ZIP overrides (compression, level, timestamps, comments, zip64)

Compression Providers

ReaderOptions and WriterOptions expose a Providers registry that controls which ICompressionProvider implementations are used for each CompressionType. The registry defaults to CompressionProviderRegistry.Default, so you only need to set it if you want to swap in a custom provider (for example the SystemGZipCompressionProvider or SystemDeflateCompressionProvider). The selected registry is honored by Reader/Writer APIs, Archive APIs, and async entry-stream extraction paths.

var registry = CompressionProviderRegistry.Default
    .With(new SystemGZipCompressionProvider())
    .With(new SystemDeflateCompressionProvider());
var readerOptions = ReaderOptions.ForFilePath.WithProviders(registry);
var writerOptions = new WriterOptions(CompressionType.GZip)
{
    CompressionLevel = 6,
}.WithProviders(registry);

using var reader = ReaderFactory.OpenReader(input, readerOptions);
using var writer = WriterFactory.OpenWriter(output, ArchiveType.GZip, writerOptions);

Registry API summary:

CompressionProviderRegistry.Default: built-in provider registry
CompressionProviderRegistry.Empty: empty registry, primarily useful for tests
With(provider): returns a new registry with that provider added or replaced
GetProvider(type): returns the registered ICompressionProvider, or null
CreateCompressStream(...): creates a compression stream or throws if unsupported
CreateDecompressStream(...): creates a decompression stream or throws if unsupported
CreateCompressStreamAsync(...): async stream creation counterpart
CreateDecompressStreamAsync(...): async stream creation counterpart
GetCompressingProvider(type): returns ICompressionProviderHooks when available

ICompressionProvider implementations declare their CompressionType, whether they support compression/decompression, and create sync/async streams. For simple providers, derive from CompressionProviderBase; it supplies default async implementations that delegate to the synchronous methods. For read-only codecs, derive from DecompressionOnlyProviderBase.

public sealed class CustomGZipProvider : CompressionProviderBase
{
    public override CompressionType CompressionType => CompressionType.GZip;
    public override bool SupportsCompression => true;
    public override bool SupportsDecompression => true;

    public override Stream CreateCompressStream(Stream destination, int compressionLevel) =>
        new GZipStream(destination, CompressionMode.Compress, leaveOpen: false);

    public override Stream CreateDecompressStream(Stream source) =>
        new GZipStream(source, CompressionMode.Decompress, leaveOpen: false);
}

CompressionContext carries metadata that providers may need when creating streams, including InputSize, OutputSize, Properties, CanSeek, FormatOptions, and ReaderOptions. Use CompressionContext.FromStream(stream) to populate stream-derived values, WithReaderOptions(...) to attach reader metadata, and ResolveArchiveEncoding() to get the archive header encoding from the context.

When a format needs additional initialization/finalization data (LZMA, PPMd, etc.) the registry exposes GetCompressingProvider which returns the ICompressionProviderHooks contract; the rest of the API continues to flow through Providers, including pre/properties/post compression hook data.


Compression Types

Available Compressions

// For creating archives
CompressionType.None       // No compression (store)
CompressionType.GZip       // GZip wrapper/combined tar compression
CompressionType.Deflate    // DEFLATE (default for ZIP/GZip)
CompressionType.Deflate64  // Deflate64
CompressionType.BZip2      // BZip2
CompressionType.LZMA       // LZMA (for 7Zip, LZip, XZ)
CompressionType.LZMA2      // LZMA2 (for 7Zip/XZ)
CompressionType.BCJ        // 7Zip branch converter filter
CompressionType.BCJ2       // 7Zip branch converter filter
CompressionType.LZip       // LZip wrapper/combined tar compression
CompressionType.Xz         // XZ wrapper/combined tar compression
CompressionType.PPMd       // PPMd (for ZIP)
CompressionType.Rar        // RAR compression (read-only)
CompressionType.Lzw        // LZW, including .Z/tar.Z reading
CompressionType.Shrink     // ZIP shrink (read-only)
CompressionType.Reduce1    // ZIP reduce methods (read-only)
CompressionType.Reduce2
CompressionType.Reduce3
CompressionType.Reduce4
CompressionType.Explode    // ZIP implode/explode (read-only)
CompressionType.Squeezed   // ARC/ARJ legacy compression (read-only)
CompressionType.Packed     // ARC legacy compression (read-only)
CompressionType.Crunched   // ARC legacy compression (read-only)
CompressionType.Squashed   // Legacy compression (read-only)
CompressionType.Crushed    // Legacy compression (read-only)
CompressionType.Distilled  // Legacy compression (read-only)
CompressionType.ZStandard  // ZStandard
CompressionType.ArjLZ77    // ARJ compression (read-only)
CompressionType.AceLZ77    // ACE compression (read-only)
CompressionType.Unknown

// For Tar archives with compression
// Use WriterFactory to create compressed tar archives
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, CompressionType.GZip))  // Tar.GZip
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, CompressionType.BZip2)) // Tar.BZip2

Archive Types

ArchiveType.Zip
ArchiveType.Tar
ArchiveType.GZip
ArchiveType.Rar
ArchiveType.SevenZip
ArchiveType.Arc
ArchiveType.Arj
ArchiveType.Ace
ArchiveType.Lzw

Patterns & Examples

Extract with Error Handling

try
{
    using (var archive = ZipArchive.OpenArchive("archive.zip", 
        ReaderOptions.ForEncryptedArchive("password")))
    {
        archive.WriteToDirectory(@"C:\output");
    }
}
catch (CryptographicException)
{
    Console.WriteLine("Password required");
}
catch (ArchiveException)
{
    Console.WriteLine("Archive is invalid");
}
catch (SharpCompressException ex)
{
    Console.WriteLine($"Error: {ex.Message}");
}

Extract with Progress

var progress = new Progress<ProgressReport>(report =>
{
    Console.WriteLine($"Extracting {report.EntryPath}: {report.PercentComplete}%");
});

var options = ReaderOptions.ForFilePath.WithProgress(progress);
using (var archive = ZipArchive.OpenArchive("archive.zip", options))
{
    archive.WriteToDirectory(@"C:\output");
}

Async Extract with Cancellation

var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromMinutes(5));

try
{
    await using (var archive = await ZipArchive.OpenAsyncArchive("archive.zip"))
    {
        await archive.WriteToDirectoryAsync(
            @"C:\output",
            cancellationToken: cts.Token
        );
    }
}
catch (OperationCanceledException)
{
    Console.WriteLine("Extraction cancelled");
}

Create with Custom Compression

using (var archive = ZipArchive.CreateArchive())
{
    archive.AddAllFromDirectory(@"D:\source");
    
    // Fastest
    archive.SaveTo("fast.zip", new WriterOptions(CompressionType.Deflate)
    {
        CompressionLevel = 1
    });
    
    // Balanced (default)
    archive.SaveTo("normal.zip", CompressionType.Deflate);
    
    // Best compression
    archive.SaveTo("best.zip", new WriterOptions(CompressionType.Deflate)
    {
        CompressionLevel = 9
    });
}

Stream Processing (No File I/O)

using (var outputStream = new MemoryStream())
using (var archive = ZipArchive.CreateArchive())
{
    // Add content from memory
    using (var contentStream = new MemoryStream(Encoding.UTF8.GetBytes("Hello")))
    {
        archive.AddEntry("file.txt", contentStream);
    }
    
    // Save to memory
    archive.SaveTo(outputStream, CompressionType.Deflate);
    
    // Get bytes
    byte[] archiveBytes = outputStream.ToArray();
}

Buffered Forward-Only Streams

SharpCompressStream can wrap streams with buffering for forward-only scenarios:

// Wrap a non-seekable stream with buffering
using (var bufferedStream = new SharpCompressStream(rawStream))
{
    // Provides ring buffer functionality for reading ahead
    // and seeking within buffered data
    using (var reader = ReaderFactory.OpenReader(bufferedStream))
    {
        while (reader.MoveToNextEntry())
        {
            reader.WriteEntryToDirectory(@"C:\output");
        }
    }
}

Useful for:

  • Non-seekable streams (network streams, pipes)
  • Forward-only reading with limited look-ahead
  • Buffering unbuffered streams for better performance

Extract Specific Files

using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
    var filesToExtract = new[] { "file1.txt", "file2.txt" };
    
    foreach (var entry in archive.Entries.Where(e => filesToExtract.Contains(e.Key)))
    {
        entry.WriteToFile(@"C:\output\" + entry.Key);
    }
}

List Archive Contents

using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
    foreach (var entry in archive.Entries)
    {
        if (entry.IsDirectory)
            Console.WriteLine($"[DIR]  {entry.Key}");
        else
            Console.WriteLine($"[FILE] {entry.Key} ({entry.Size} bytes)");
    }
}

Common Mistakes

✗ Wrong - Stream not disposed

var stream = File.OpenRead("archive.zip");
var archive = ZipArchive.OpenArchive(stream);
archive.WriteToDirectory(@"C:\output");
// stream not disposed - leaked resource

✓ Correct - Using blocks

using (var stream = File.OpenRead("archive.zip"))
using (var archive = ZipArchive.OpenArchive(stream))
{
    archive.WriteToDirectory(@"C:\output");
}
// Both properly disposed

✗ Wrong - Mixing API styles

// Loading entire archive then iterating
using (var archive = ZipArchive.OpenArchive("large.zip"))
{
    var entries = archive.Entries.ToList();  // Loads all in memory
    foreach (var e in entries)
    {
        e.WriteToFile(...);  // Then extracts each
    }
}

✓ Correct - Use Reader for large files

// Streaming iteration
using (var stream = File.OpenRead("large.zip"))
using (var reader = ReaderFactory.OpenReader(stream))
{
    while (reader.MoveToNextEntry())
    {
        reader.WriteEntryToDirectory(@"C:\output");
    }
}