Зробіть архів 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);
Після цього потік innerCompressed містить сам внутрішній архів. Конструктор архіву дозволяє розпакувати наданий потік. Отже, ми також можемо витягти його:
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 }