updates from review

This commit is contained in:
Adam Hathcock
2026-06-18 17:06:16 +01:00
parent e8ee054757
commit 727d2a79f9
4 changed files with 112 additions and 18 deletions

View File

@@ -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))
{
// ...

View File

@@ -18,7 +18,19 @@ public static class IArchiveEntryExtensions
/// <param name="streamToWriteTo">The stream to write the entry content to.</param>
/// <param name="progress">Optional progress reporter for tracking extraction progress.</param>
public void WriteTo(Stream streamToWriteTo, IProgress<ProgressReport>? progress = null) =>
archiveEntry.WriteTo(streamToWriteTo, null, progress: progress);
archiveEntry.WriteTo(streamToWriteTo, bufferSize: null, progress: progress);
/// <summary>
/// Extract entry to the specified stream.
/// </summary>
/// <param name="streamToWriteTo">The stream to write the entry content to.</param>
/// <param name="options">Options for configuring extraction behavior.</param>
/// <param name="progress">Optional progress reporter for tracking extraction progress.</param>
public void WriteTo(
Stream streamToWriteTo,
ExtractionOptions options,
IProgress<ProgressReport>? 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<ProgressReport>? progress = null,
CancellationToken cancellationToken = default
)
{
) =>
await archiveEntry
.WriteToAsync(
streamToWriteTo,
@@ -60,7 +71,29 @@ public static class IArchiveEntryExtensions
cancellationToken: cancellationToken
)
.ConfigureAwait(false);
}
/// <summary>
/// Extract entry to the specified stream asynchronously.
/// </summary>
/// <param name="streamToWriteTo">The stream to write the entry content to.</param>
/// <param name="options">Options for configuring extraction behavior.</param>
/// <param name="progress">Optional progress reporter for tracking extraction progress.</param>
/// <param name="cancellationToken">Cancellation token.</param>
public async ValueTask WriteToAsync(
Stream streamToWriteTo,
ExtractionOptions options,
IProgress<ProgressReport>? progress = null,
CancellationToken cancellationToken = default
) =>
await archiveEntry
.WriteToAsync(
streamToWriteTo,
options.BufferSize,
options,
progress,
cancellationToken
)
.ConfigureAwait(false);
private async ValueTask WriteToAsync(
Stream streamToWriteTo,

View File

@@ -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<int> ReadAsync(
byte[] buffer,

View File

@@ -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<InvalidFormatException>(() =>
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<InvalidFormatException>(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()
{