Files
sharpcompress/docs/API.md

930 lines
34 KiB
Markdown
Raw Permalink Normal View History

2026-01-08 15:34:48 +00:00
# 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.
```csharp
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.
2026-01-08 15:34:48 +00:00
## Factory Methods
### Opening Archives
```csharp
// Auto-detect format
2026-01-15 13:29:57 +00:00
using (var reader = ReaderFactory.OpenReader(stream))
2026-01-08 15:34:48 +00:00
{
// Works with Zip, Tar, GZip, Rar, etc. Reading 7Zip needs the Archive API
2026-01-08 15:34:48 +00:00
}
// Specific format - Archive API
2026-01-15 13:29:57 +00:00
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"))
2026-01-08 15:34:48 +00:00
2026-02-10 11:10:04 +00:00
// 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
2026-01-12 16:08:16 +00:00
{
2026-01-08 15:34:48 +00:00
Password = "password",
LeaveStreamOpen = true,
ArchiveEncoding = new ArchiveEncoding { Default = Encoding.GetEncoding(932) }
};
```
```csharp
// 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}");
2026-08-05 16:20:08 +01:00
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`.
2026-10-01 16:54:03 +01:00
`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.
2026-08-06 09:13:22 +01:00
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)` |
2026-08-05 16:20:08 +01:00
| `ZipDataDescriptorEntryCount` | `Zip.HasEntriesWithDeferredSizes` |
| `SolidStreamCount` | `SolidStreamCount` |
2026-10-01 16:54:03 +01:00
### Creating Archives with the Writer API
2026-01-08 15:34:48 +00:00
```csharp
// Writer Factory
2026-01-15 13:29:57 +00:00
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate))
2026-01-08 15:34:48 +00:00
{
// Write entries
}
// Specific writer
2026-01-15 13:04:09 +00:00
using (var archive = ZipArchive.CreateArchive())
using (var archive = TarArchive.CreateArchive())
using (var archive = GZipArchive.CreateArchive())
2026-01-08 15:34:48 +00:00
2026-02-10 11:10:04 +00:00
// With fluent options (preferred)
var options = WriterOptions.ForZip()
.WithCompressionLevel(9)
2026-06-12 14:41:05 +01:00
.WithLeaveStreamOpen(false)
.WithBufferSize(131072);
2026-01-15 13:04:09 +00:00
using (var archive = ZipArchive.CreateArchive())
2026-01-08 15:34:48 +00:00
{
archive.SaveTo("output.zip", options);
}
2026-02-10 11:10:04 +00:00
// Alternative: constructor with object initializer
var options2 = new WriterOptions(CompressionType.Deflate)
{
CompressionLevel = 9,
2026-06-12 14:41:05 +01:00
LeaveStreamOpen = false,
BufferSize = 131072
2026-02-10 11:10:04 +00:00
};
2026-01-08 15:34:48 +00:00
```
2026-06-12 14:41:05 +01:00
`WriterOptions.BufferSize` controls stream copy buffers used while writing archive entries. If it is not set, SharpCompress falls back to `Constants.BufferSize`.
2026-01-08 15:34:48 +00:00
---
## Archive API Methods
### Reading/Extracting
```csharp
2026-01-15 13:29:57 +00:00
using (var archive = ZipArchive.OpenArchive("file.zip"))
2026-01-08 15:34:48 +00:00
{
// Get all entries
2026-01-12 16:08:16 +00:00
IEnumerable<IArchiveEntry> entries = archive.Entries;
2026-01-08 15:34:48 +00:00
// Find specific entry
var entry = archive.Entries.FirstOrDefault(e => e.Key == "file.txt");
2026-01-12 16:08:16 +00:00
2026-01-08 15:34:48 +00:00
// Extract all
archive.WriteToDirectory(@"C:\output");
2026-01-12 16:08:16 +00:00
2026-01-08 15:34:48 +00:00
// Extract single entry
var firstEntry = archive.Entries.First();
firstEntry.WriteToFile(@"C:\output\file.txt");
2026-01-12 16:08:16 +00:00
2026-06-18 17:06:16 +01:00
// 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 }
);
}
2026-01-08 15:34:48 +00:00
// Get entry stream
using (var stream = entry.OpenEntryStream())
{
stream.CopyTo(outputStream);
}
}
2026-01-12 16:08:16 +00:00
// Async extraction (requires IAsyncArchive)
2026-02-12 10:32:20 +00:00
await using (var asyncArchive = await ZipArchive.OpenAsyncArchive("file.zip"))
2026-01-12 16:08:16 +00:00
{
// Extract all entries asynchronously
2026-01-12 16:08:16 +00:00
await asyncArchive.WriteToDirectoryAsync(
@"C:\output",
cancellationToken: cancellationToken
);
}
// Open a specific entry stream asynchronously
await using (var asyncArchive = await ZipArchive.OpenAsyncArchive("file.zip"))
2026-01-08 15:34:48 +00:00
{
await foreach (var entry in asyncArchive.EntriesAsync)
{
2026-06-18 17:06:16 +01:00
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))
{
// ...
}
}
2026-01-08 15:34:48 +00:00
}
```
### Entry Properties
```csharp
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;
}
```
2026-10-01 16:54:03 +01:00
### Creating Archives with the Archive API
2026-01-08 15:34:48 +00:00
```csharp
2026-01-15 13:04:09 +00:00
using (var archive = ZipArchive.CreateArchive())
2026-01-08 15:34:48 +00:00
{
// Add file
2026-01-12 16:08:16 +00:00
archive.AddEntry("file.txt", @"C:\source\file.txt");
2026-01-08 15:34:48 +00:00
// Add multiple files
2026-01-12 16:08:16 +00:00
archive.AddAllFromDirectory(@"C:\source");
archive.AddAllFromDirectory(@"C:\source", "*.txt"); // Pattern
2026-01-08 15:34:48 +00:00
// Save to file
archive.SaveTo("output.zip", CompressionType.Deflate);
2026-01-12 16:08:16 +00:00
2026-01-08 15:34:48 +00:00
// Save to stream
archive.SaveTo(outputStream, new WriterOptions(CompressionType.Deflate)
{
CompressionLevel = 9,
LeaveStreamOpen = true
});
}
```
---
## Reader API Methods
### Forward-Only Reading
```csharp
using (var stream = File.OpenRead("file.zip"))
2026-01-15 13:29:57 +00:00
using (var reader = ReaderFactory.OpenReader(stream))
2026-01-08 15:34:48 +00:00
{
while (reader.MoveToNextEntry())
{
IEntry entry = reader.Entry;
2026-01-12 16:08:16 +00:00
2026-01-08 15:34:48 +00:00
if (!entry.IsDirectory)
{
// Extract entry
reader.WriteEntryToDirectory(@"C:\output");
reader.WriteEntryToFile(@"C:\output\file.txt");
2026-01-12 16:08:16 +00:00
2026-01-08 15:34:48 +00:00
// Or get stream
using (var entryStream = reader.OpenEntryStream())
{
entryStream.CopyTo(outputStream);
}
}
}
}
2026-01-15 13:04:09 +00:00
// Async variants (use OpenAsyncReader to get IAsyncReader)
2026-01-12 16:08:16 +00:00
using (var stream = File.OpenRead("file.zip"))
2026-02-12 10:32:20 +00:00
await using (var reader = await ReaderFactory.OpenAsyncReader(stream))
2026-01-08 15:34:48 +00:00
{
2026-01-12 16:08:16 +00:00
while (await reader.MoveToNextEntryAsync())
{
await reader.WriteEntryToFileAsync(
@"C:\output\" + reader.Entry.Key,
cancellationToken: cancellationToken
);
}
2026-01-08 15:34:48 +00:00
2026-01-12 16:08:16 +00:00
// Async extraction of all entries
await reader.WriteAllToDirectoryAsync(
@"C:\output",
cancellationToken: cancellationToken
2026-01-12 16:08:16 +00:00
);
}
2026-01-08 15:34:48 +00:00
```
---
## Writer API Methods
### Creating Archives (Streaming)
```csharp
using (var stream = File.Create("output.zip"))
2026-01-15 13:29:57 +00:00
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate))
2026-01-08 15:34:48 +00:00
{
// 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);
2026-01-08 15:34:48 +00:00
```
---
## Common Options
### ReaderOptions
2026-03-03 12:50:27 +00:00
Use preset properties and fluent helpers for common configurations:
2026-02-10 11:10:04 +00:00
2026-01-08 15:34:48 +00:00
```csharp
2026-02-10 11:10:04 +00:00
// External stream with password and custom encoding
2026-03-03 12:50:27 +00:00
var options = ReaderOptions.ForExternalStream
2026-02-10 11:10:04 +00:00
.WithPassword("password")
.WithArchiveEncoding(new ArchiveEncoding { Default = Encoding.GetEncoding(932) });
2026-01-15 13:29:57 +00:00
using (var archive = ZipArchive.OpenArchive("file.zip", options))
2026-01-08 15:34:48 +00:00
{
// ...
}
// Open-time presets
var external = ReaderOptions.ForExternalStream;
2026-03-03 12:40:58 +00:00
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
2026-03-03 12:40:58 +00:00
var metadataOptions = ExtractionOptions.PreserveMetadata; // Keep timestamps and attributes
2026-02-10 11:10:04 +00:00
// Tune extraction copy buffering
2026-06-15 09:17:05 +01:00
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
2026-01-08 15:34:48 +00:00
```
2026-02-10 11:10:04 +00:00
Alternative: traditional object initializer:
```csharp
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,
2026-02-10 11:10:04 +00:00
};
var extractionOptions = new ExtractionOptions
{
ExtractFullPath = true,
Overwrite = true,
BufferSize = 131072,
};
2026-02-10 11:10:04 +00:00
```
2026-01-08 15:34:48 +00:00
### WriterOptions
2026-02-10 11:10:04 +00:00
Factory methods provide a clean, discoverable way to create writer options:
```csharp
// 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:
```csharp
// 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(),
};
```
2026-02-10 11:10:04 +00:00
Alternative: traditional constructor with object initializer:
2026-01-08 15:34:48 +00:00
```csharp
var options = new WriterOptions(CompressionType.Deflate)
{
2026-02-10 11:10:04 +00:00
CompressionLevel = 9,
LeaveStreamOpen = true,
2026-01-08 15:34:48 +00:00
};
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
2026-01-08 15:34:48 +00:00
```csharp
var options = new ExtractionOptions
2026-01-08 15:34:48 +00:00
{
ExtractFullPath = true, // Recreate directory structure
Overwrite = true, // Overwrite existing files
2026-06-15 09:17:05 +01:00
PreserveFileTime = true, // Keep original timestamps
CheckCrc = true // Validate payload checksums when available
2026-01-08 15:34:48 +00:00
};
using (var archive = ZipArchive.OpenArchive("file.zip"))
{
archive.WriteToDirectory(@"C:\output", options);
}
```
2026-06-15 09:17:05 +01:00
`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
```text
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)
2026-01-08 15:34:48 +00:00
```
2026-02-10 15:51:50 +00:00
### Compression Providers
2026-06-07 11:20:34 +01:00
`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.
2026-02-10 15:51:50 +00:00
```csharp
2026-06-07 11:20:34 +01:00
var registry = CompressionProviderRegistry.Default
.With(new SystemGZipCompressionProvider())
.With(new SystemDeflateCompressionProvider());
2026-03-03 12:40:58 +00:00
var readerOptions = ReaderOptions.ForFilePath.WithProviders(registry);
2026-02-10 15:51:50 +00:00
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);
```
2026-06-07 11:20:34 +01:00
Registry API summary:
```text
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`.
```csharp
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.
2026-02-10 15:51:50 +00:00
2026-01-08 15:34:48 +00:00
---
## Compression Types
### Available Compressions
```csharp
// For creating archives
CompressionType.None // No compression (store)
CompressionType.GZip // GZip wrapper/combined tar compression
2026-01-08 15:34:48 +00:00
CompressionType.Deflate // DEFLATE (default for ZIP/GZip)
2026-01-12 16:08:16 +00:00
CompressionType.Deflate64 // Deflate64
2026-01-08 15:34:48 +00:00
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
2026-01-08 15:34:48 +00:00
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)
2026-01-12 16:08:16 +00:00
CompressionType.ZStandard // ZStandard
CompressionType.ArjLZ77 // ARJ compression (read-only)
CompressionType.AceLZ77 // ACE compression (read-only)
CompressionType.Unknown
2026-01-12 16:08:16 +00:00
// For Tar archives with compression
// Use WriterFactory to create compressed tar archives
2026-01-15 13:29:57 +00:00
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, CompressionType.GZip)) // Tar.GZip
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, CompressionType.BZip2)) // Tar.BZip2
2026-01-08 15:34:48 +00:00
```
### Archive Types
```csharp
ArchiveType.Zip
ArchiveType.Tar
ArchiveType.GZip
ArchiveType.Rar
ArchiveType.SevenZip
ArchiveType.Arc
ArchiveType.Arj
ArchiveType.Ace
ArchiveType.Lzw
2026-01-08 15:34:48 +00:00
```
---
## Patterns & Examples
### Extract with Error Handling
```csharp
try
{
using (var archive = ZipArchive.OpenArchive("archive.zip",
2026-02-10 11:10:04 +00:00
ReaderOptions.ForEncryptedArchive("password")))
2026-01-08 15:34:48 +00:00
{
archive.WriteToDirectory(@"C:\output");
2026-01-08 15:34:48 +00:00
}
}
catch (CryptographicException)
2026-01-08 15:34:48 +00:00
{
Console.WriteLine("Password required");
}
catch (ArchiveException)
2026-01-08 15:34:48 +00:00
{
Console.WriteLine("Archive is invalid");
}
catch (SharpCompressException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
```
### Extract with Progress
```csharp
var progress = new Progress<ProgressReport>(report =>
{
Console.WriteLine($"Extracting {report.EntryPath}: {report.PercentComplete}%");
});
2026-03-03 12:40:58 +00:00
var options = ReaderOptions.ForFilePath.WithProgress(progress);
2026-01-15 13:29:57 +00:00
using (var archive = ZipArchive.OpenArchive("archive.zip", options))
2026-01-08 15:34:48 +00:00
{
archive.WriteToDirectory(@"C:\output");
}
```
### Async Extract with Cancellation
```csharp
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromMinutes(5));
try
{
2026-02-12 10:32:20 +00:00
await using (var archive = await ZipArchive.OpenAsyncArchive("archive.zip"))
2026-01-08 15:34:48 +00:00
{
2026-01-12 16:08:16 +00:00
await archive.WriteToDirectoryAsync(
@"C:\output",
cancellationToken: cts.Token
);
2026-01-08 15:34:48 +00:00
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Extraction cancelled");
}
```
### Create with Custom Compression
```csharp
2026-01-15 13:04:09 +00:00
using (var archive = ZipArchive.CreateArchive())
2026-01-08 15:34:48 +00:00
{
archive.AddAllFromDirectory(@"D:\source");
// Fastest
2026-01-15 13:04:09 +00:00
archive.SaveTo("fast.zip", new WriterOptions(CompressionType.Deflate)
{
CompressionLevel = 1
2026-01-08 15:34:48 +00:00
});
// Balanced (default)
archive.SaveTo("normal.zip", CompressionType.Deflate);
// Best compression
2026-01-15 13:04:09 +00:00
archive.SaveTo("best.zip", new WriterOptions(CompressionType.Deflate)
{
CompressionLevel = 9
2026-01-08 15:34:48 +00:00
});
}
```
### Stream Processing (No File I/O)
```csharp
using (var outputStream = new MemoryStream())
2026-01-15 13:04:09 +00:00
using (var archive = ZipArchive.CreateArchive())
2026-01-08 15:34:48 +00:00
{
// 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();
}
```
2026-02-14 10:26:33 +00:00
### Buffered Forward-Only Streams
`SharpCompressStream` can wrap streams with buffering for forward-only scenarios:
```csharp
// 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:
2026-10-01 16:54:03 +01:00
2026-02-14 10:26:33 +00:00
- Non-seekable streams (network streams, pipes)
- Forward-only reading with limited look-ahead
- Buffering unbuffered streams for better performance
2026-01-08 15:34:48 +00:00
### Extract Specific Files
```csharp
2026-01-15 13:29:57 +00:00
using (var archive = ZipArchive.OpenArchive("archive.zip"))
2026-01-08 15:34:48 +00:00
{
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
```csharp
2026-01-15 13:29:57 +00:00
using (var archive = ZipArchive.OpenArchive("archive.zip"))
2026-01-08 15:34:48 +00:00
{
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
```csharp
var stream = File.OpenRead("archive.zip");
2026-01-15 13:29:57 +00:00
var archive = ZipArchive.OpenArchive(stream);
2026-01-08 15:34:48 +00:00
archive.WriteToDirectory(@"C:\output");
// stream not disposed - leaked resource
```
### ✓ Correct - Using blocks
```csharp
using (var stream = File.OpenRead("archive.zip"))
2026-01-15 13:29:57 +00:00
using (var archive = ZipArchive.OpenArchive(stream))
2026-01-08 15:34:48 +00:00
{
archive.WriteToDirectory(@"C:\output");
}
// Both properly disposed
```
### ✗ Wrong - Mixing API styles
```csharp
// Loading entire archive then iterating
2026-01-15 13:29:57 +00:00
using (var archive = ZipArchive.OpenArchive("large.zip"))
2026-01-08 15:34:48 +00:00
{
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
```csharp
// Streaming iteration
using (var stream = File.OpenRead("large.zip"))
2026-01-15 13:29:57 +00:00
using (var reader = ReaderFactory.OpenReader(stream))
2026-01-08 15:34:48 +00:00
{
while (reader.MoveToNextEntry())
{
reader.WriteEntryToDirectory(@"C:\output");
}
}
```
---
## Related Documentation
- [USAGE.md](USAGE.md) - Complete code examples
- [FORMATS.md](FORMATS.md) - Supported formats
- [PERFORMANCE.md](PERFORMANCE.md) - API selection guide