diff --git a/docs/API.md b/docs/API.md
index 79745918..db807f55 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -132,6 +132,15 @@ using (var archive = ZipArchive.OpenArchive("file.zip"))
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())
{
@@ -154,6 +163,15 @@ 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))
{
// ...
diff --git a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs
index a9c3a054..502a419e 100644
--- a/src/SharpCompress/Archives/IArchiveEntryExtensions.cs
+++ b/src/SharpCompress/Archives/IArchiveEntryExtensions.cs
@@ -18,7 +18,19 @@ public static class IArchiveEntryExtensions
/// The stream to write the entry content to.
/// Optional progress reporter for tracking extraction progress.
public void WriteTo(Stream streamToWriteTo, IProgress? progress = null) =>
- archiveEntry.WriteTo(streamToWriteTo, null, progress: progress);
+ archiveEntry.WriteTo(streamToWriteTo, bufferSize: null, progress: progress);
+
+ ///
+ /// Extract entry to the specified stream.
+ ///
+ /// The stream to write the entry content to.
+ /// Options for configuring extraction behavior.
+ /// Optional progress reporter for tracking extraction progress.
+ public void WriteTo(
+ Stream streamToWriteTo,
+ ExtractionOptions options,
+ IProgress? progress = null
+ ) => archiveEntry.WriteTo(streamToWriteTo, options.BufferSize, options, progress);
private void WriteTo(
Stream streamToWriteTo,
@@ -50,8 +62,7 @@ public static class IArchiveEntryExtensions
Stream streamToWriteTo,
IProgress? progress = null,
CancellationToken cancellationToken = default
- )
- {
+ ) =>
await archiveEntry
.WriteToAsync(
streamToWriteTo,
@@ -60,7 +71,29 @@ public static class IArchiveEntryExtensions
cancellationToken: cancellationToken
)
.ConfigureAwait(false);
- }
+
+ ///
+ /// Extract entry to the specified stream asynchronously.
+ ///
+ /// The stream to write the entry content to.
+ /// Options for configuring extraction behavior.
+ /// Optional progress reporter for tracking extraction progress.
+ /// Cancellation token.
+ public async ValueTask WriteToAsync(
+ Stream streamToWriteTo,
+ ExtractionOptions options,
+ IProgress? progress = null,
+ CancellationToken cancellationToken = default
+ ) =>
+ await archiveEntry
+ .WriteToAsync(
+ streamToWriteTo,
+ options.BufferSize,
+ options,
+ progress,
+ cancellationToken
+ )
+ .ConfigureAwait(false);
private async ValueTask WriteToAsync(
Stream streamToWriteTo,
diff --git a/src/SharpCompress/Common/ChecksumValidationStream.cs b/src/SharpCompress/Common/ChecksumValidationStream.cs
index e3c4e657..1b14cc9d 100644
--- a/src/SharpCompress/Common/ChecksumValidationStream.cs
+++ b/src/SharpCompress/Common/ChecksumValidationStream.cs
@@ -56,20 +56,7 @@ internal sealed class ChecksumValidationStream : Stream
}
#endif
- public override int ReadByte()
- {
- var value = _stream.ReadByte();
- if (value == -1)
- {
- Validate();
- }
- else
- {
- UpdateChecksum([(byte)value]);
- }
-
- return value;
- }
+ public override int ReadByte() => throw new NotSupportedException();
public override async Task ReadAsync(
byte[] buffer,
diff --git a/tests/SharpCompress.Test/Zip/ZipCrcExtractionTests.cs b/tests/SharpCompress.Test/Zip/ZipCrcExtractionTests.cs
index 14a55fc6..3020e70b 100644
--- a/tests/SharpCompress.Test/Zip/ZipCrcExtractionTests.cs
+++ b/tests/SharpCompress.Test/Zip/ZipCrcExtractionTests.cs
@@ -45,6 +45,34 @@ public class ZipCrcExtractionTests : ArchiveTests
Assert.Equal(EntryData, File.ReadAllBytes(destination));
}
+ [Fact]
+ public void Zip_Archive_WriteTo_Throws_On_Crc_Mismatch_When_Enabled()
+ {
+ using var zipStream = CreateZipWithInvalidCrc(useDataDescriptor: false);
+ using var archive = ZipArchive.OpenArchive(zipStream);
+ var entry = archive.Entries.Single(e => !e.IsDirectory);
+ using var destination = new MemoryStream();
+
+ var exception = Assert.Throws(() =>
+ entry.WriteTo(destination, new ExtractionOptions { CheckCrc = true })
+ );
+
+ Assert.Contains(EntryName, exception.Message);
+ }
+
+ [Fact]
+ public void Zip_Archive_WriteTo_Skips_Crc_Mismatch_When_Disabled()
+ {
+ using var zipStream = CreateZipWithInvalidCrc(useDataDescriptor: false);
+ using var archive = ZipArchive.OpenArchive(zipStream);
+ var entry = archive.Entries.Single(e => !e.IsDirectory);
+ using var destination = new MemoryStream();
+
+ entry.WriteTo(destination, new ExtractionOptions { CheckCrc = false });
+
+ Assert.Equal(EntryData, destination.ToArray());
+ }
+
[Fact]
public void Zip_Reader_WriteEntryToFile_Throws_On_Crc_Mismatch()
{
@@ -88,6 +116,34 @@ public class ZipCrcExtractionTests : ArchiveTests
Assert.Contains(EntryName, exception.Message);
}
+ [Fact]
+ public async Task Zip_Archive_WriteToAsync_Throws_On_Crc_Mismatch_When_Enabled()
+ {
+ using var zipStream = CreateZipWithInvalidCrc(useDataDescriptor: false);
+ using var archive = ZipArchive.OpenArchive(zipStream);
+ var entry = archive.Entries.Single(e => !e.IsDirectory);
+ await using var destination = new MemoryStream();
+
+ var exception = await Assert.ThrowsAsync(async () =>
+ await entry.WriteToAsync(destination, new ExtractionOptions { CheckCrc = true })
+ );
+
+ Assert.Contains(EntryName, exception.Message);
+ }
+
+ [Fact]
+ public async Task Zip_Archive_WriteToAsync_Skips_Crc_Mismatch_When_Disabled()
+ {
+ using var zipStream = CreateZipWithInvalidCrc(useDataDescriptor: false);
+ using var archive = ZipArchive.OpenArchive(zipStream);
+ var entry = archive.Entries.Single(e => !e.IsDirectory);
+ await using var destination = new MemoryStream();
+
+ await entry.WriteToAsync(destination, new ExtractionOptions { CheckCrc = false });
+
+ Assert.Equal(EntryData, destination.ToArray());
+ }
+
[Fact]
public async Task Zip_Reader_WriteEntryToFileAsync_Throws_On_Crc_Mismatch()
{