Сделать ZIP-архив плоским

Внутри вашего zip-архива могут быть другие zip-архивы. Возможно, вам захочется извлечь содержимое вложенного zip-архива в родительский архив, чтобы получить плоскую структуру.

Текущая структура архива
outer.zip
 ├first.txt
 ├inner.zip
 │ ├game.exe
 │ └subitem.bin
 └picture.gif
Желаемая структура архива
flatten.zip
 ├first.txt
 ├picture.gif
 ├game.exe
 └subitem.bin

Если вы не знакомы с Aspose.Zip, сначала прочтите, как распаковать zip-архив.

Общее объяснение

Для начала нам нужно перечислить все записи архива. Обычные записи следует сохранять такими, какие они есть, нам даже не следует их распаковывать. Записи, которые сами по себе являются архивами, необходимо извлечь в память и удалить из внешнего архива. Их содержимое необходимо включить в основной архив.

Обнаружение записей, являющихся архивами

Давайте решим, какие записи сами являются архивами. Мы можем понять это, расширив имя записи. Позже мы удалим эти записи из основного архива, поэтому храните такие записи в списке.

1if (entry.Name.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase)) {
2    entriesToDelete.Add(entry);
3    ...
4}

Извлечение содержимого записи в память

Aspose.Zip позволяет извлекать содержимое zip-файла в любой записываемый поток, а не только в файл. Итак, мы можем извлечь вложенный архив в поток памяти.

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

1MemoryStream innerCompressed = new MemoryStream();
2entry.Open().CopyTo(innerCompressed); 

После этого поток InternalCompressed содержит сам внутренний архив. Конструктор архива позволяет распаковать предоставленный поток. Итак, мы также можем извлечь его:

1Archive inner = new Archive(innerCompressed);

Исключая записи

Мы можем удалить запись из zip-архива с помощью определенного метода.

1foreach (ArchiveEntry e in entriesToDelete) { outer.DeleteEntry(e); }

Соедините всё это

Вот полный алгоритм.

 1    using (Archive outer = new Archive("outer.zip"))
 2    {
 3        List<ArchiveEntry> entriesToDelete = new List<ArchiveEntry>();
 4        List<string> namesToInsert = new List<string>();
 5        List<MemoryStream> contentToInsert = new List<MemoryStream>();
 6
 7        foreach (ArchiveEntry entry in outer.Entries)
 8        {
 9            if (entry.Name.EndsWith(".zip", StringComparison.InvariantCultureIgnoreCase)) // Find an entry which is an archive itself
10            {
11                entriesToDelete.Add(entry); // Keep reference to the entry in order to remove it from the archive later
12                MemoryStream innerCompressed = new MemoryStream();
13                entry.Open().CopyTo(innerCompressed); //This extracts the entry to a memory stream
14
15                using (Archive inner = new Archive(innerCompressed)) // We know that content of the entry is an zip archive so we may extract
16                {
17                    foreach (ArchiveEntry ie in inner.Entries) // Loop over entries of inner archive
18                    {
19                        namesToInsert.Add(ie.Name); // Keep the name of inner entry.
20                        MemoryStream content = new MemoryStream();
21                        ie.Open().CopyTo(content);
22                        contentToInsert.Add(content); // Keep the content of inner entry.
23                    }
24                }
25            }
26        }
27
28        foreach (ArchiveEntry e in entriesToDelete) 
29            outer.DeleteEntry(e); // Delete all the entries which are archives itself
30
31        for (int i = 0; i < namesToInsert.Count; i++)    
32            outer.CreateEntry(namesToInsert[i], contentToInsert[i]); // Adds entries which were entries of inner archives
33        
34        outer.Save("flatten.zip");
35    }

Subscribe to Aspose Product Updates

Get monthly newsletters & offers directly delivered to your mailbox.