从另一个zip文件重新创建zip文件

时间:2017-01-26 18:31:04

标签: c# asp.net ziparchive

给定一个zip文件,我需要使用指定的压缩级别重新创建它(例如,不压缩)。

我快到了,但得到了错误:

  

失败:End Of Central Directory中预期的条目数与中央目录中的条目数不对应。

如果我将重新创建的zip文件保存到Windows,它看起来是正确的(正确的文件大小,条目都存在正确的文件大小),但没有一个文件是可提取的。

public static byte[] ReCompress(byte[] originalArchive, CompressionLevel newCompressionLevel)
{
    var entries = new Dictionary<string, byte[]>();

    ///////////////////////////
    // STEP 1: EXTRACT ALL FILES
    ///////////////////////////
    using (var ms = new MemoryStream(originalArchive))
    using (var originalZip = new ZipArchive(ms, ZipArchiveMode.Read))
    {
        foreach (var entry in originalZip.Entries)
        {
            var isFolder = entry.FullName.EndsWith("/");
            if (!isFolder)
            {
                using (var stream = entry.Open())
                using (var entryMS = new MemoryStream())
                {
                    stream.CopyTo(entryMS);
                    entries.Add(entry.FullName, entryMS.ToArray());
                }

            }
            else
            {
                entries.Add(entry.FullName, new byte[0]);
            }
        }
    }

    ///////////////////////////
    // STEP 2: BUILD ZIP FILE
    ///////////////////////////
    using (var ms = new MemoryStream())
    using (var newArchive = new ZipArchive(ms, ZipArchiveMode.Create, true))
    {
        foreach (var uncompressedEntry in entries)
        {
            var newEntry = newArchive.CreateEntry(uncompressedEntry.Key, newCompressionLevel);
            using (var entryStream = newEntry.Open())
            using (var writer = new BinaryWriter(entryStream, Encoding.UTF8))
            {
                writer.Write(uncompressedEntry.Value);
            }
        }
        return ms.ToArray();
    }
}

如果我这样做,在函数结束时:

File.WriteAllBytes(@"D:\test.zip", ms.ToArray());

它会创建一个大小为90mb的正确结构存档,但不能提取任何文件。

如果我以return ms.ToArray()结束,则返回~130kb字节数组。

1 个答案:

答案 0 :(得分:1)

Zip存档已损坏,因为您在完成之前从MemoryStream读取了其内容。要完成存档创建,您需要在致电ms.ToArray()之前致电newArchive.Dispose()。 在这种特殊情况下,您可以这样做:

using (var ms = new MemoryStream())
{
    using (var newArchive = new ZipArchive(ms, ZipArchiveMode.Create, true))
    {
        foreach (var uncompressedEntry in entries)
        {
            var newEntry = newArchive.CreateEntry(uncompressedEntry.Key, newCompressionLevel);
            using (var entryStream = newEntry.Open())
            using (var writer = new BinaryWriter(entryStream, Encoding.UTF8))
            {
                writer.Write(uncompressedEntry.Value);
            }
        }
    }

    return ms.ToArray();
}