Tom Gullen
Tom Gullen

Reputation: 61755

Re-create zip file from another zip file

Given a zip file, I need to re-create it with a specified compression level (eg, no compression).

I'm nearly there, but get the error:

Failed: Number of entries expected in End Of Central Directory does not correspond to number of entries in Central Directory.

If I save the recreated zip file to windows, it looks like it's correct (correct file size, entries all exist with correct file sizes) but none of the files are extractable.

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();
    }
}

At the end of the function if I do:

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

It creates a correctly structure archive sized 90mb but no files are extractable.

If I end with return ms.ToArray() it returns a ~130kb byte array.

Upvotes: 0

Views: 898

Answers (1)

SuMMeR
SuMMeR

Reputation: 97

Zip archive is broken because you read its content from MemoryStream before it is finished. In order to finish archive creation you need to call newArchive.Dispose() before calling ms.ToArray(). In this particular case you can do it like this:

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();
}

Upvotes: 1

Related Questions