ZipArchive 无法使用 MemoryStream 正确压缩文件

ZipArchive does not properly compress files with MemoryStream

我正在使用 .NET 4.5 中的 ZipArchive,它可以与 FileStream:

一起正常工作
public static byte[] CompressWithFiles(string dir)
{
    var archiveName = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".zip");    
    var files = Directory.GetFiles(testsConsoleDir);
    using (var stream = new FileStream(archiveName, FileMode.Create))
    {
        using (var archive = new ZipArchive(stream, ZipArchiveMode.Create))
        {
            foreach (var file in files)
                archive.CreateEntryFromFile(file, Path.GetFileName(file), CompressionLevel.Optimal);
        }
    }
    byte[] result = File.ReadAllBytes(archiveName);

    return result;
}

但是如果我试图用 MemoryStream 压缩文件,它 returns 无法打开的字节数组被保存到磁盘:

public static byte[] CompressInMemory(string dir)
{
    var files = Directory.GetFiles(dir);
    byte[] result = null;
    using (var stream = new MemoryStream())
    {
        using (var archive = new ZipArchive(stream, ZipArchiveMode.Create))
        {
            foreach (var file in files)
                archive.CreateEntryFromFile(file, Path.GetFileName(file), CompressionLevel.Optimal);

            stream.Position = 0;
            result = new byte[stream.Length];
            stream.Read(result, 0, (int)stream.Length);
        }
    }

    return result;
}

我比较了第一种和第二种方法的数组,发现它们是相等的,除了第一个数组末尾有一个剩余字节,这使得 zip 存档可以打开。如何在 CompressInMemory 方法中解决此问题?我试图避免数据存储在磁盘上。

您必须"close" zip 才能阅读:

using (var archive = new ZipArchive(stream, ZipArchiveMode.Create, true))
{
    foreach (var file in files)
        archive.CreateEntryFromFile(file, Path.GetFileName(file), CompressionLevel.Optimal);
}

stream.Position = 0;
result = new byte[stream.Length];
stream.Read(result, 0, (int)stream.Length);        

// Note that you can replace the previous 3 rows
// from stream.Position = 0 onward) with:
//result = stream.ToArray();

但是您必须设置 ZipArchive 以在关闭时离开流 "non-disposed"(最后的 true)。

通过这种方式,zip "flushed" 到流中。如果您注意到您在 FileStream 示例中做对了。

通常你应该stream.Flush() stream.Position = 0之前,因为stream可能有一些未写的内部缓冲区,但是MemoryStream 这不是必需的,因为 MemoryStream.Flush()

Overrides the Stream.Flush method so that no action is performed.