mirror of
https://github.com/adamhathcock/sharpcompress.git
synced 2026-02-04 05:25:00 +00:00
adds more async tests and overloads to make things writable and async
This commit is contained in:
@@ -2,6 +2,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
using SharpCompress.Writers;
|
||||
@@ -141,6 +143,18 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
|
||||
SaveTo(stream, options, OldEntries, newEntries);
|
||||
}
|
||||
|
||||
public async Task SaveToAsync(
|
||||
Stream stream,
|
||||
WriterOptions options,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
//reset streams of new entries
|
||||
newEntries.Cast<IWritableArchiveEntry>().ForEach(x => x.Stream.Seek(0, SeekOrigin.Begin));
|
||||
await SaveToAsync(stream, options, OldEntries, newEntries, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
protected TEntry CreateEntry(
|
||||
string key,
|
||||
Stream source,
|
||||
@@ -173,6 +187,14 @@ public abstract class AbstractWritableArchive<TEntry, TVolume>
|
||||
IEnumerable<TEntry> newEntries
|
||||
);
|
||||
|
||||
protected abstract Task SaveToAsync(
|
||||
Stream stream,
|
||||
WriterOptions options,
|
||||
IEnumerable<TEntry> oldEntries,
|
||||
IEnumerable<TEntry> newEntries,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
|
||||
public override void Dispose()
|
||||
{
|
||||
base.Dispose();
|
||||
|
||||
@@ -2,6 +2,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.GZip;
|
||||
using SharpCompress.IO;
|
||||
@@ -136,6 +138,16 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
SaveTo(stream, new WriterOptions(CompressionType.GZip));
|
||||
}
|
||||
|
||||
public Task SaveToAsync(string filePath, CancellationToken cancellationToken = default) =>
|
||||
SaveToAsync(new FileInfo(filePath), cancellationToken);
|
||||
|
||||
public async Task SaveToAsync(FileInfo fileInfo, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
await SaveToAsync(stream, new WriterOptions(CompressionType.GZip), cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static bool IsGZipFile(Stream stream)
|
||||
{
|
||||
// read the header on the first read
|
||||
@@ -196,6 +208,28 @@ public class GZipArchive : AbstractWritableArchive<GZipArchiveEntry, GZipVolume>
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task SaveToAsync(
|
||||
Stream stream,
|
||||
WriterOptions options,
|
||||
IEnumerable<GZipArchiveEntry> oldEntries,
|
||||
IEnumerable<GZipArchiveEntry> newEntries,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (Entries.Count > 1)
|
||||
{
|
||||
throw new InvalidFormatException("Only one entry is allowed in a GZip Archive");
|
||||
}
|
||||
using var writer = new GZipWriter(stream, new GZipWriterOptions(options));
|
||||
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
await writer
|
||||
.WriteAsync(entry.Key.NotNull("Entry Key is null"), entryStream, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<GZipArchiveEntry> LoadEntries(IEnumerable<GZipVolume> volumes)
|
||||
{
|
||||
var stream = volumes.Single().Stream;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.IO;
|
||||
|
||||
@@ -30,6 +32,34 @@ public static class IArchiveEntryExtensions
|
||||
streamListener.FireEntryExtractionEnd(archiveEntry);
|
||||
}
|
||||
|
||||
public static async Task WriteToAsync(
|
||||
this IArchiveEntry archiveEntry,
|
||||
Stream streamToWriteTo,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
if (archiveEntry.IsDirectory)
|
||||
{
|
||||
throw new ExtractionException("Entry is a file directory and cannot be extracted.");
|
||||
}
|
||||
|
||||
var streamListener = (IArchiveExtractionListener)archiveEntry.Archive;
|
||||
streamListener.EnsureEntriesLoaded();
|
||||
streamListener.FireEntryExtractionBegin(archiveEntry);
|
||||
streamListener.FireFilePartExtractionBegin(
|
||||
archiveEntry.Key ?? "Key",
|
||||
archiveEntry.Size,
|
||||
archiveEntry.CompressedSize
|
||||
);
|
||||
var entryStream = archiveEntry.OpenEntryStream();
|
||||
using (entryStream)
|
||||
{
|
||||
using Stream s = new ListeningStream(streamListener, entryStream);
|
||||
await s.CopyToAsync(streamToWriteTo, 81920, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
streamListener.FireEntryExtractionEnd(archiveEntry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extract to specific directory, retaining filename
|
||||
/// </summary>
|
||||
@@ -63,4 +93,24 @@ public static class IArchiveEntryExtensions
|
||||
entry.WriteTo(fs);
|
||||
}
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Extract to specific file asynchronously
|
||||
/// </summary>
|
||||
public static Task WriteToFileAsync(
|
||||
this IArchiveEntry entry,
|
||||
string destinationFileName,
|
||||
ExtractionOptions? options = null,
|
||||
CancellationToken cancellationToken = default
|
||||
) =>
|
||||
ExtractionMethods.WriteEntryToFileAsync(
|
||||
entry,
|
||||
destinationFileName,
|
||||
options,
|
||||
async (x, fm) =>
|
||||
{
|
||||
using var fs = File.Open(destinationFileName, fm);
|
||||
await entry.WriteToAsync(fs, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Writers;
|
||||
|
||||
namespace SharpCompress.Archives;
|
||||
@@ -18,6 +20,12 @@ public interface IWritableArchive : IArchive
|
||||
|
||||
void SaveTo(Stream stream, WriterOptions options);
|
||||
|
||||
Task SaveToAsync(
|
||||
Stream stream,
|
||||
WriterOptions options,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Use this to pause entry rebuilding when adding large collections of entries. Dispose when complete. A using statement is recommended.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Writers;
|
||||
|
||||
namespace SharpCompress.Archives;
|
||||
@@ -42,6 +44,24 @@ public static class IWritableArchiveExtensions
|
||||
writableArchive.SaveTo(stream, options);
|
||||
}
|
||||
|
||||
public static Task SaveToAsync(
|
||||
this IWritableArchive writableArchive,
|
||||
string filePath,
|
||||
WriterOptions options,
|
||||
CancellationToken cancellationToken = default
|
||||
) => writableArchive.SaveToAsync(new FileInfo(filePath), options, cancellationToken);
|
||||
|
||||
public static async Task SaveToAsync(
|
||||
this IWritableArchive writableArchive,
|
||||
FileInfo fileInfo,
|
||||
WriterOptions options,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
using var stream = fileInfo.Open(FileMode.Create, FileAccess.Write);
|
||||
await writableArchive.SaveToAsync(stream, options, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public static void AddAllFromDirectory(
|
||||
this IWritableArchive writableArchive,
|
||||
string filePath,
|
||||
|
||||
@@ -2,6 +2,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Tar;
|
||||
using SharpCompress.Common.Tar.Headers;
|
||||
@@ -242,6 +244,24 @@ public class TarArchive : AbstractWritableArchive<TarArchiveEntry, TarVolume>
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task SaveToAsync(
|
||||
Stream stream,
|
||||
WriterOptions options,
|
||||
IEnumerable<TarArchiveEntry> oldEntries,
|
||||
IEnumerable<TarArchiveEntry> newEntries,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
using var writer = new TarWriter(stream, new TarWriterOptions(options));
|
||||
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
await writer
|
||||
.WriteAsync(entry.Key.NotNull("Entry Key is null"), entryStream, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected override IReader CreateReaderForSolidExtraction()
|
||||
{
|
||||
var stream = Volumes.Single().Stream;
|
||||
|
||||
@@ -2,6 +2,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Common.Zip;
|
||||
using SharpCompress.Common.Zip.Headers;
|
||||
@@ -317,6 +319,24 @@ public class ZipArchive : AbstractWritableArchive<ZipArchiveEntry, ZipVolume>
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task SaveToAsync(
|
||||
Stream stream,
|
||||
WriterOptions options,
|
||||
IEnumerable<ZipArchiveEntry> oldEntries,
|
||||
IEnumerable<ZipArchiveEntry> newEntries,
|
||||
CancellationToken cancellationToken = default
|
||||
)
|
||||
{
|
||||
using var writer = new ZipWriter(stream, new ZipWriterOptions(options));
|
||||
foreach (var entry in oldEntries.Concat(newEntries).Where(x => !x.IsDirectory))
|
||||
{
|
||||
using var entryStream = entry.OpenEntryStream();
|
||||
await writer
|
||||
.WriteAsync(entry.Key.NotNull("Entry Key is null"), entryStream, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected override ZipArchiveEntry CreateEntryInternal(
|
||||
string filePath,
|
||||
Stream source,
|
||||
|
||||
127
tests/SharpCompress.Test/GZip/GZipArchiveAsyncTests.cs
Normal file
127
tests/SharpCompress.Test/GZip/GZipArchiveAsyncTests.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Archives;
|
||||
using SharpCompress.Archives.GZip;
|
||||
using SharpCompress.Archives.Tar;
|
||||
using SharpCompress.Common;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.GZip;
|
||||
|
||||
public class GZipArchiveAsyncTests : ArchiveTests
|
||||
{
|
||||
public GZipArchiveAsyncTests() => UseExtensionInsteadOfNameToVerify = true;
|
||||
|
||||
[Fact]
|
||||
public async Task GZip_Archive_Generic_Async()
|
||||
{
|
||||
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
|
||||
using (var archive = ArchiveFactory.Open(stream))
|
||||
{
|
||||
var entry = archive.Entries.First();
|
||||
await entry.WriteToFileAsync(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull()));
|
||||
|
||||
var size = entry.Size;
|
||||
var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"));
|
||||
var test = new FileInfo(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"));
|
||||
|
||||
Assert.Equal(size, scratch.Length);
|
||||
Assert.Equal(size, test.Length);
|
||||
}
|
||||
CompareArchivesByPath(
|
||||
Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GZip_Archive_Async()
|
||||
{
|
||||
using (Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
|
||||
using (var archive = GZipArchive.Open(stream))
|
||||
{
|
||||
var entry = archive.Entries.First();
|
||||
await entry.WriteToFileAsync(Path.Combine(SCRATCH_FILES_PATH, entry.Key.NotNull()));
|
||||
|
||||
var size = entry.Size;
|
||||
var scratch = new FileInfo(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"));
|
||||
var test = new FileInfo(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"));
|
||||
|
||||
Assert.Equal(size, scratch.Length);
|
||||
Assert.Equal(size, test.Length);
|
||||
}
|
||||
CompareArchivesByPath(
|
||||
Path.Combine(SCRATCH_FILES_PATH, "Tar.tar"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar")
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GZip_Archive_NoAdd_Async()
|
||||
{
|
||||
var jpg = Path.Combine(ORIGINAL_FILES_PATH, "jpg", "test.jpg");
|
||||
using Stream stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"));
|
||||
using var archive = GZipArchive.Open(stream);
|
||||
Assert.Throws<InvalidFormatException>(() => archive.AddEntry("jpg\\test.jpg", jpg));
|
||||
await archive.SaveToAsync(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GZip_Archive_Multiple_Reads_Async()
|
||||
{
|
||||
var inputStream = new MemoryStream();
|
||||
using (var fileStream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")))
|
||||
{
|
||||
await fileStream.CopyToAsync(inputStream);
|
||||
inputStream.Position = 0;
|
||||
}
|
||||
using var archive = GZipArchive.Open(inputStream);
|
||||
var archiveEntry = archive.Entries.First();
|
||||
|
||||
MemoryStream tarStream;
|
||||
using (var entryStream = archiveEntry.OpenEntryStream())
|
||||
{
|
||||
tarStream = new MemoryStream();
|
||||
await entryStream.CopyToAsync(tarStream);
|
||||
}
|
||||
var size = tarStream.Length;
|
||||
using (var entryStream = archiveEntry.OpenEntryStream())
|
||||
{
|
||||
tarStream = new MemoryStream();
|
||||
await entryStream.CopyToAsync(tarStream);
|
||||
}
|
||||
Assert.Equal(size, tarStream.Length);
|
||||
using (var entryStream = archiveEntry.OpenEntryStream())
|
||||
{
|
||||
var result = TarArchive.IsTarFile(entryStream);
|
||||
Assert.True(result);
|
||||
}
|
||||
Assert.Equal(size, tarStream.Length);
|
||||
using (var entryStream = archiveEntry.OpenEntryStream())
|
||||
{
|
||||
tarStream = new MemoryStream();
|
||||
await entryStream.CopyToAsync(tarStream);
|
||||
}
|
||||
Assert.Equal(size, tarStream.Length);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestGzCrcWithMostSignificantBitNotNegative_Async()
|
||||
{
|
||||
using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"));
|
||||
using var archive = GZipArchive.Open(stream);
|
||||
foreach (var entry in archive.Entries.Where(entry => !entry.IsDirectory))
|
||||
{
|
||||
Assert.InRange(entry.Crc, 0L, 0xFFFFFFFFL);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void TestGzArchiveTypeGzip_Async()
|
||||
{
|
||||
using var stream = File.OpenRead(Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz"));
|
||||
using var archive = GZipArchive.Open(stream);
|
||||
Assert.Equal(archive.Type, ArchiveType.GZip);
|
||||
}
|
||||
}
|
||||
83
tests/SharpCompress.Test/GZip/GZipWriterAsyncTests.cs
Normal file
83
tests/SharpCompress.Test/GZip/GZipWriterAsyncTests.cs
Normal file
@@ -0,0 +1,83 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using SharpCompress.Common;
|
||||
using SharpCompress.Writers;
|
||||
using SharpCompress.Writers.GZip;
|
||||
using Xunit;
|
||||
|
||||
namespace SharpCompress.Test.GZip;
|
||||
|
||||
public class GZipWriterAsyncTests : WriterTests
|
||||
{
|
||||
public GZipWriterAsyncTests()
|
||||
: base(ArchiveType.GZip) => UseExtensionInsteadOfNameToVerify = true;
|
||||
|
||||
[Fact]
|
||||
public async Task GZip_Writer_Generic_Async()
|
||||
{
|
||||
using (
|
||||
Stream stream = File.Open(
|
||||
Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"),
|
||||
FileMode.OpenOrCreate,
|
||||
FileAccess.Write
|
||||
)
|
||||
)
|
||||
using (var writer = WriterFactory.Open(stream, ArchiveType.GZip, CompressionType.GZip))
|
||||
{
|
||||
await writer.WriteAsync("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"));
|
||||
}
|
||||
CompareArchivesByPath(
|
||||
Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task GZip_Writer_Async()
|
||||
{
|
||||
using (
|
||||
Stream stream = File.Open(
|
||||
Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"),
|
||||
FileMode.OpenOrCreate,
|
||||
FileAccess.Write
|
||||
)
|
||||
)
|
||||
using (var writer = new GZipWriter(stream))
|
||||
{
|
||||
await writer.WriteAsync("Tar.tar", Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar"));
|
||||
}
|
||||
CompareArchivesByPath(
|
||||
Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")
|
||||
);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void GZip_Writer_Generic_Bad_Compression_Async() =>
|
||||
Assert.Throws<InvalidFormatException>(() =>
|
||||
{
|
||||
using Stream stream = File.OpenWrite(Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"));
|
||||
using var writer = WriterFactory.Open(stream, ArchiveType.GZip, CompressionType.BZip2);
|
||||
});
|
||||
|
||||
[Fact]
|
||||
public async Task GZip_Writer_Entry_Path_With_Dir_Async()
|
||||
{
|
||||
using (
|
||||
Stream stream = File.Open(
|
||||
Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"),
|
||||
FileMode.OpenOrCreate,
|
||||
FileAccess.Write
|
||||
)
|
||||
)
|
||||
using (var writer = new GZipWriter(stream))
|
||||
{
|
||||
var path = Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar");
|
||||
await writer.WriteAsync(path, path);
|
||||
}
|
||||
CompareArchivesByPath(
|
||||
Path.Combine(SCRATCH_FILES_PATH, "Tar.tar.gz"),
|
||||
Path.Combine(TEST_ARCHIVES_PATH, "Tar.tar.gz")
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user