Files
sharpcompress/docs/USAGE.md

354 lines
12 KiB
Markdown
Raw Permalink Normal View History

2016-09-29 10:55:04 +01:00
# SharpCompress Usage
2026-01-08 15:34:48 +00:00
## Async/Await Support (Beta)
SharpCompress now provides full async/await support for all I/O operations. All `Read`, `Write`, and extraction operations have async equivalents ending in `Async` that accept an optional `CancellationToken`. This enables better performance and scalability for I/O-bound operations.
**Key Async Methods:**
- `reader.WriteEntryToAsync(stream, cancellationToken)` - Extract entry asynchronously
- `reader.WriteAllToDirectoryAsync(path, cancellationToken: cancellationToken)` - Extract all asynchronously
- `writer.WriteAsync(filename, stream, modTime, cancellationToken)` - Write entry asynchronously
- `writer.WriteAllAsync(directory, pattern, searchOption, cancellationToken)` - Write directory asynchronously
- `entry.OpenEntryStreamAsync(cancellationToken)` - Open entry stream asynchronously
See [Async Examples](#async-examples) section below for usage patterns.
2026-01-08 15:34:48 +00:00
## Stream Rules
2018-03-15 08:55:06 +00:00
2018-05-16 08:51:33 +01:00
When dealing with Streams, the rule should be that you don't close a stream you didn't create. This, in effect, should mean you should always put a Stream in a using block to dispose it.
2016-09-29 10:55:04 +01:00
However, the .NET Framework often has classes that will dispose streams by default to make things "easy" like the following:
```C#
using (var reader = new StreamReader(File.Open("foo")))
{
...
}
```
2018-03-15 08:55:06 +00:00
In this example, reader should get disposed. However, stream rules should say the the `FileStream` created by `File.Open` should remain open. However, the .NET Framework closes it for you by default unless you override the constructor. In general, you should be writing Stream code like this:
2016-09-29 10:55:04 +01:00
```C#
using (var fileStream = File.Open("foo"))
using (var reader = new StreamReader(fileStream))
{
...
}
```
2018-05-16 08:51:33 +01:00
To deal with the "correct" rules as well as the expectations of users, I've decided to always close wrapped streams as of 0.21.
2016-09-29 10:55:04 +01:00
To be explicit though, consider always using the overloads that use `ReaderOptions` or `WriterOptions` and explicitly set `LeaveStreamOpen` the way you want.
Default behavior in factory APIs:
- File path / `FileInfo` overloads set `LeaveStreamOpen = false`.
- Caller-provided `Stream` overloads set `LeaveStreamOpen = true`.
2024-08-29 12:11:19 +03:00
If using Compression Stream classes directly and you don't want the wrapped stream to be closed. Use the `NonDisposingStream` as a wrapper to prevent the stream being disposed. The change in 0.21 simplified a lot even though the usage is a bit more convoluted.
2018-05-16 08:51:33 +01:00
2016-09-29 10:55:04 +01:00
## Samples
2018-05-05 15:08:56 +01:00
Also, look over the tests for more thorough [examples](https://github.com/adamhathcock/sharpcompress/tree/master/tests/SharpCompress.Test)
2016-09-29 11:10:11 +01:00
### Create Zip Archive from multiple files
```C#
2026-01-15 13:04:09 +00:00
using(var archive = ZipArchive.CreateArchive())
{
archive.AddEntry("file01.txt", "C:\\file01.txt");
archive.AddEntry("file02.txt", "C:\\file02.txt");
...
archive.SaveTo("C:\\temp.zip", CompressionType.Deflate);
}
```
2016-09-29 10:55:04 +01:00
### Create Zip Archive from all files in a directory to a file
```C#
2026-01-15 13:04:09 +00:00
using (var archive = ZipArchive.CreateArchive())
2016-09-29 10:55:04 +01:00
{
archive.AddAllFromDirectory("D:\\temp");
archive.SaveTo("C:\\temp.zip", CompressionType.Deflate);
}
```
### Create Zip Archive from all files in a directory and save in memory
```C#
var memoryStream = new MemoryStream();
2026-01-15 13:04:09 +00:00
using (var archive = ZipArchive.CreateArchive())
2016-09-29 10:55:04 +01:00
{
archive.AddAllFromDirectory("D:\\temp");
archive.SaveTo(memoryStream, new WriterOptions(CompressionType.Deflate)
{
LeaveStreamOpen = true
});
}
//reset memoryStream to be usable now
memoryStream.Position = 0;
```
### Extract all files from a rar file to a directory using RarArchive
Note: Extracting a solid rar or 7z file needs to be done in sequential order to get acceptable decompression speed.
`ExtractAllEntries` is primarily intended for solid archives (like solid Rar) or 7Zip archives, where sequential extraction provides the best performance. For general/simple extraction with any supported archive type, use `archive.WriteToDirectory()` instead.
2016-09-29 10:55:04 +01:00
```C#
// Use ReaderOptions for open-time behavior and ExtractionOptions for extract-time behavior
2026-03-03 12:40:58 +00:00
using (var archive = RarArchive.OpenArchive("Test.rar", ReaderOptions.ForFilePath))
2016-09-29 10:55:04 +01:00
{
// Simple extraction with RarArchive; this WriteToDirectory pattern works for all archive types
archive.WriteToDirectory(
@"D:\temp",
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
);
2016-09-29 10:55:04 +01:00
}
```
### Iterate over all files from a Rar file using RarArchive
```C#
2026-01-15 13:29:57 +00:00
using (var archive = RarArchive.OpenArchive("Test.rar"))
{
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
{
Console.WriteLine($"{entry.Key}: {entry.Size} bytes");
}
}
```
2026-01-07 15:30:26 +00:00
### Extract solid Rar or 7Zip archives with progress reporting
2025-12-23 09:34:55 +00:00
`ExtractAllEntries` only works for solid archives (Rar) or 7Zip archives. For optimal performance with these archive types, use this method:
```C#
2026-01-07 15:30:26 +00:00
using SharpCompress.Common;
using SharpCompress.Readers;
var progress = new Progress<ProgressReport>(report =>
{
Console.WriteLine($"Extracting {report.EntryPath}: {report.PercentComplete}%");
});
2026-02-10 11:10:04 +00:00
using (var archive = RarArchive.OpenArchive("archive.rar",
2026-03-03 12:40:58 +00:00
ReaderOptions.ForFilePath
.WithProgress(progress))) // Must be solid Rar or 7Zip
{
archive.WriteToDirectory(
@"D:\output",
new ExtractionOptions { ExtractFullPath = true, Overwrite = true }
);
}
```
2016-09-29 10:55:04 +01:00
### Use ReaderFactory to autodetect archive type and Open the entry stream
```C#
using (Stream stream = File.OpenRead("Tar.tar.bz2"))
2026-01-15 13:29:57 +00:00
using (var reader = ReaderFactory.OpenReader(stream))
2016-09-29 10:55:04 +01:00
{
while (reader.MoveToNextEntry())
{
if (!reader.Entry.IsDirectory)
{
Console.WriteLine(reader.Entry.Key);
reader.WriteEntryToDirectory(@"C:\temp");
2016-09-29 10:55:04 +01:00
}
}
}
```
### Use ReaderFactory to autodetect archive type and Open the entry stream
```C#
using (Stream stream = File.OpenRead("Tar.tar.bz2"))
2026-01-15 13:29:57 +00:00
using (var reader = ReaderFactory.OpenReader(stream))
2016-09-29 10:55:04 +01:00
{
while (reader.MoveToNextEntry())
{
if (!reader.Entry.IsDirectory)
{
using (var entryStream = reader.OpenEntryStream())
{
entryStream.CopyTo(...);
}
}
}
}
```
### Use WriterFactory to write all files from a directory in a streaming manner.
```C#
using (Stream stream = File.OpenWrite("C:\\temp.tgz"))
2026-02-10 11:10:04 +00:00
using (var writer = WriterFactory.OpenWriter(
stream,
ArchiveType.Tar,
WriterOptions.ForTar(CompressionType.GZip).WithLeaveStreamOpen(true)))
2016-09-29 10:55:04 +01:00
{
writer.WriteAll("D:\\temp", "*", SearchOption.AllDirectories);
}
```
2018-05-23 09:46:36 +09:00
### Extract zip which has non-utf8 encoded filename(cp932)
```C#
var encoding = Encoding.GetEncoding(932);
2026-02-10 11:10:04 +00:00
var opts = new ReaderOptions()
.WithArchiveEncoding(new ArchiveEncoding
{
CustomDecoder = (data, x, y) => encoding.GetString(data)
});
using var archive = ZipArchive.OpenArchive("test.zip", opts);
foreach(var entry in archive.Entries)
2018-05-23 09:46:36 +09:00
{
Console.WriteLine($"{entry.Key}");
}
```
2026-02-10 15:51:50 +00:00
## Custom Compression Providers
By default `ReaderOptions` and `WriterOptions` already include `CompressionProviderRegistry.Default` via their `Providers` property, so you can read and write without touching the registry yet still get SharpCompresss built-in implementations.
The configured registry is used consistently across Reader APIs, Writer APIs, Archive APIs, and async entry-stream extraction, including compressed TAR wrappers and ZIP async decompression.
2026-02-10 15:51:50 +00:00
To replace a specific algorithm (for example to use `System.IO.Compression` for GZip or Deflate), create a modified registry and pass it through the same options:
```C#
var systemGZip = new SystemGZipCompressionProvider();
var customRegistry = CompressionProviderRegistry.Default.With(systemGZip);
2026-03-03 12:40:58 +00:00
var readerOptions = ReaderOptions.ForFilePath
2026-02-10 15:51:50 +00:00
.WithProviders(customRegistry);
using var reader = ReaderFactory.OpenReader(stream, readerOptions);
var writerOptions = new WriterOptions(CompressionType.GZip)
.WithProviders(customRegistry);
using var writer = WriterFactory.OpenWriter(outputStream, ArchiveType.GZip, writerOptions);
```
The registry also exposes `GetCompressingProvider` (now returning `ICompressionProviderHooks`) when a compression format needs pre- or post-stream data (e.g., LZMA/PPMd). Implementations that need extra headers can supply those bytes through the `ICompressionProviderHooks` members while the rest of the API still works through the `Providers` property.
## Async Examples
### Async Reader Examples
**Extract single entry asynchronously:**
```C#
using (Stream stream = File.OpenRead("archive.zip"))
2026-01-15 13:29:57 +00:00
using (var reader = ReaderFactory.OpenReader(stream))
{
while (reader.MoveToNextEntry())
{
if (!reader.Entry.IsDirectory)
{
using (var entryStream = reader.OpenEntryStream())
{
using (var outputStream = File.Create("output.bin"))
{
await reader.WriteEntryToAsync(outputStream, cancellationToken);
}
}
}
}
}
```
**Extract all entries asynchronously:**
```C#
using (Stream stream = File.OpenRead("archive.tar.gz"))
2026-01-15 13:29:57 +00:00
using (var reader = ReaderFactory.OpenReader(stream))
{
await reader.WriteAllToDirectoryAsync(
@"D:\temp",
cancellationToken: cancellationToken
);
}
```
**Open and process entry stream asynchronously:**
```C#
2026-01-15 13:29:57 +00:00
using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
foreach (var entry in archive.Entries.Where(e => !e.IsDirectory))
{
using (var entryStream = await entry.OpenEntryStreamAsync(cancellationToken))
{
// Process the decompressed stream asynchronously
await ProcessStreamAsync(entryStream, cancellationToken);
}
}
}
```
### Async Writer Examples
**Write single file asynchronously:**
```C#
using (Stream archiveStream = File.OpenWrite("output.zip"))
2026-01-15 13:29:57 +00:00
using (var writer = WriterFactory.OpenWriter(archiveStream, ArchiveType.Zip, CompressionType.Deflate))
{
using (Stream fileStream = File.OpenRead("input.txt"))
{
await writer.WriteAsync("entry.txt", fileStream, DateTime.Now, cancellationToken);
}
}
```
**Write entire directory asynchronously:**
```C#
using (Stream stream = File.OpenWrite("backup.tar.gz"))
2026-01-15 13:29:57 +00:00
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Tar, new WriterOptions(CompressionType.GZip)))
{
await writer.WriteAllAsync(
@"D:\files",
"*",
SearchOption.AllDirectories,
cancellationToken
);
}
```
**Write with progress tracking and cancellation:**
```C#
var cts = new CancellationTokenSource();
// Set timeout or cancel from UI
cts.CancelAfter(TimeSpan.FromMinutes(5));
using (Stream stream = File.OpenWrite("archive.zip"))
2026-01-15 13:29:57 +00:00
using (var writer = WriterFactory.OpenWriter(stream, ArchiveType.Zip, CompressionType.Deflate))
{
try
{
await writer.WriteAllAsync(@"D:\data", "*", SearchOption.AllDirectories, cts.Token);
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation was cancelled");
}
}
```
### Archive Async Examples
**Extract from archive asynchronously:**
```C#
2026-01-15 13:29:57 +00:00
using (var archive = ZipArchive.OpenArchive("archive.zip"))
{
2025-12-23 09:34:55 +00:00
// Simple async extraction - works for all archive types
await archive.WriteToDirectoryAsync(
@"C:\output",
cancellationToken: cancellationToken
2025-12-23 09:34:55 +00:00
);
}
```
**Benefits of Async Operations:**
- Non-blocking I/O for better application responsiveness
- Improved scalability for server applications
- Support for cancellation via CancellationToken
- Better resource utilization in async/await contexts
- Compatible with modern .NET async patterns