Восстановить zip-файл из другого zip-файла

Учитывая zip-файл, мне нужно воссоздать его с указанным уровнем сжатия (например, без сжатия).

Я почти там, но получаю ошибку:

Ошибка: ожидаемое количество записей в конце центрального каталога не соответствует количеству записей в центральном каталоге.

Если я сохраню воссозданный 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 байтов.


person Tom Gullen    schedule 26.01.2017    source источник
comment
Что ж, долгим путем было бы извлечь первый архив во временное место и повторно сжать его в новый архив.   -  person Nkosi    schedule 26.01.2017


Ответы (1)


Zip-архив поврежден, потому что вы прочитали его содержимое из MemoryStream до его завершения. Для завершения создания архива необходимо вызвать newArchive.Dispose(). перед вызовом ms.ToArray(). В этом конкретном случае вы можете сделать это следующим образом:

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();
}
person SuMMeR    schedule 26.01.2017