Ответ 1
Я просмотрел ваш код и сразу обнаружил проблемы. В настоящее время проблема с большим количеством разработчиков программного обеспечения заключается в том, что они в настоящее время не понимают, как работает материал, что делает невозможным рассуждать об этом. В этом конкретном случае вы, похоже, не знаете, как работают файлы ZIP; поэтому я предлагаю вам сначала прочитать о том, как они работают, и попытался сломать то, что происходит под капотом.
Рассуждение
Теперь, когда мы все на одной странице о том, как они работают, позвольте начать рассуждать, разбив, как это работает, используя исходный код; мы продолжим оттуда вперед:
var listAes = Directory.EnumerateFiles(myFolder, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".aes")).Select(f => new FileInfo(f));
foreach (var additionFile in listAes)
{
// (1)
using (var zip = ZipFile.Read(nameOfExistingZip))
{
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.None;
// (2)
zip.AddFile(additionFile.FullName);
// (3)
zip.Save();
}
file.WriteLine("Delay for adding a file : " + sw.Elapsed.TotalMilliseconds);
sw.Restart();
}
- (1) открывает ZIP файл. Вы делаете это для каждого файла, который вы пытаетесь добавить
- (2) Добавляет один файл в ZIP файл
- (3) Сохраняет полный ZIP файл
На моем компьютере это занимает около часа.
Теперь не все данные формата файла актуальны. Мы ищем материал, который будет все хуже в вашей программе.
Сокращаясь по спецификации формата файла, вы заметите, что сжатие основано на Deflate, которое не требует информации о других сжатых файлах. Двигаясь дальше, мы заметим, как "таблица файлов" хранится в ZIP файле:
Вы заметите, что там есть "центральный каталог", который хранит файлы в ZIP файле. Он в основном хранится как "список". Таким образом, используя эту информацию, мы можем объяснить, что тривиальный способ обновить ее при реализации шагов (1-3) в следующем порядке:
- Откройте zip файл, прочитайте центральный каталог
- Добавить данные для (нового) сжатого файла, сохранить указатель вместе с именем файла в новом центральном каталоге.
- Перезапишите центральный каталог.
Подумайте об этом на мгновение, для файла № 1 вам потребуется 1 операция записи; для файла # 2 вам необходимо прочитать (1 элемент), добавить (в память) и написать (2 элемента); для файла № 3 вам необходимо прочитать (2 элемента), добавить (в память) и написать (3 элемента). И так далее. Это означает, что производительность будет снижаться, если вы добавите больше файлов. Вы уже это заметили, теперь вы знаете, почему.
Возможное решение
В предыдущем решении я добавил сразу все файлы. Это может не работать в вашем случае использования. Другим решением является реализация слияния, которое в каждом случае объединяет 2 файла. Это более удобно, если у вас нет всех файлов, доступных при запуске процесса сжатия.
В основном алгоритм становится следующим:
- Добавьте несколько (скажем, 16 файлов). Вы можете играть с этим номером. Сохраните это в файле -say- 'file16.zip'.
- Добавьте больше файлов. Когда вы нажимаете 16 файлов, вы должны объединить два файла из 16 элементов в один файл из 32 элементов.
- Объединить файлы, пока вы больше не сможете их объединить. В основном каждый раз, когда у вас есть два файла из N элементов, вы создаете новый файл из 2 * N элементов.
- Перейти (2).
Опять же, мы можем рассуждать об этом. Первые 16 файлов не проблема, мы уже установили это.
Мы также можем рассуждать о том, что произойдет в нашей программе. Поскольку мы объединяем 2 файла в 1 файл, нам не нужно делать столько чтения и записи. На самом деле, если вы рассудите об этом, вы увидите, что у вас есть файл из 32 записей в 2 слияния, 64 в 4 слияния, 128 в 8 слияниях, 256 из 16 слияний... эй, подождите, мы знаем эту последовательность, это 2^N
. Опять же, рассуждая об этом, мы обнаружим, что нам нужно около 500 слияний, что намного лучше, чем 200 000 операций, с которых мы начали.
Взлом в ZIP-архиве
Еще одно решение, которое может прийти на ум, - это комбинировать центральный каталог, создавая свободное место для будущих записей. Тем не менее, это, вероятно, требует, чтобы вы взломали почтовый индекс и создали свой собственный файл ZIP-записи. Идея состоит в том, что вы в основном зацикливаете центральную директорию на 200K записей, прежде чем начать, чтобы вы могли просто добавить на место.
Опять же, мы можем рассуждать об этом: добавление файла теперь означает: добавление файла и обновление некоторых заголовков. Это будет не так быстро, как исходное решение, потому что вам понадобится случайный диск IO, но он, вероятно, будет работать достаточно быстро.
Я не справился с этим, но мне это не кажется слишком сложным.
Самое простое решение - наиболее практичный
То, что мы не обсуждали до сих пор, - это самое простое решение: один подход, который приходит на ум, - просто добавить все файлы сразу, о чем мы снова можем рассуждать.
Реализация довольно проста, потому что теперь нам не нужно делать какие-либо причудливые вещи; мы можем просто использовать обработчик ZIP (я использую ионный) как есть:
static void Main()
{
try { File.Delete(@"c:\tmp\test.zip"); }
catch { }
var sw = Stopwatch.StartNew();
using (var zip = new ZipFile(@"c:\tmp\test.zip"))
{
zip.UseZip64WhenSaving = Zip64Option.Always;
for (int i = 0; i < 200000; ++i)
{
string filename = "foo" + i.ToString() + ".txt";
byte[] contents = Encoding.UTF8.GetBytes("Hello world!");
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.None;
zip.AddEntry(filename, contents);
}
zip.Save();
}
Console.WriteLine("Elapsed: {0:0.00}s", sw.Elapsed.TotalSeconds);
Console.ReadLine();
}
одолевать; который заканчивается через 4,5 секунды. Гораздо лучше.