2014-12-12 4 views
2

в .Net 4.5 класс System.IO.Compression.ZipArchive получает некоторые обновления.System.IO.Compression.ZipArchive управление памятью

Как можно читать здесь (http://msdn.microsoft.com/en-us/magazine/jj133817.aspx), теперь он должен выполнять «типичные операции, не требующие чтения всего архива в память».

Для тестирования я пытаюсь сжать 10 файлов, каждый размер 200 МБ.

Это хорошо работает, если вы создаете новые почтовые архивы с этим кодом (низкое потребление памяти за весь процесс):

for (int directoryGroupIndex = 0; directoryGroupIndex < directoryGroups.Count; directoryGroupIndex++) 
{ 
    String directoryGroupKey = directoryGroups.Keys.ElementAt(directoryGroupIndex); 
    FileInfo[] directoryGroup = directoryGroups[directoryGroupKey]; 

    String archiveFileName = String.Format("Readed Logfiles{0}", archiveFileExtension); 
    String archiveFileFullName = Path.Combine(directoryGroupKey, archiveFileName); 
    FileInfo archiveFile = new FileInfo(archiveFileFullName); 


    using (FileStream archiveFileStream = new FileStream(archiveFile.FullName, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Read)) 
    using (ZipArchive archive = new ZipArchive(archiveFileStream, ZipArchiveMode.Create, false)) 
    { 
    for (int directoryGroupFileIndex = 0; directoryGroupFileIndex < directoryGroup.Length; directoryGroupFileIndex++) 
    { 
     FileInfo file = directoryGroup[directoryGroupFileIndex]; 
     String archiveEntryName = file.Name; 
     String archiveEntryPath = DateTime.Now.ToString("yyyy-MM-dd"); 
     String archiveEntryFullName = Path.Combine(archiveEntryPath, archiveEntryName); 

     ZipArchiveEntry archiveEntry = archive.CreateEntryFromFile(file.FullName, archiveEntryFullName, CompressionLevel.Optimal); 
    } 
    }    
} 

Теперь я хочу добавлять новые записи в этот архив. Я оставляю свой код так, как есть, и запускаю его снова. (с новыми файлами внутри корневого каталога). Если я загляну в документальный документ, то я прочитал «Разрешено только создание новых записей в архиве», что все, что я хочу. Поэтому мой код должен быть в порядке.

Результат теперь:

  1. таблица файлов внутри архива перезаписываются (только новые файлы перечислены).

  2. Размер архивного файла вырос (как и старые, все еще там).

  3. Архив поврежден. Вы можете открыть его, но вы не можете декодировать контент.

Если изменить ZipArchiveMode на «ZipArchiveMode.Update» она работает, как ожидалось, но только с небольшими файлами. Файлы, подобные моему, выбрасывают исключение из памяти, поскольку полный архив загружается в память.

Мой вопрос сейчас. Я идиот и делаю это неправильно, это ошибка, или это ошибка дизайна.

Это приводит меня в норму!

Спасибо от германии за помощь Штеффен

ответ

3

Код, который вы написали, что вызывает ZipArchive класс, чтобы написать целый новый архив в конце вашего предыдущего, который, конечно, портит файл.

Способ сделать то, что вы хотите, это скопировать исходный архив в новый файл при его создании, а затем заменить оригинал на новый. Например:

string tempFile = Path.GetTempFileName(); 

using (ZipArchive original = 
    new ZipArchive(File.Open(archiveFileStream, FileMode.Open), ZipArchiveMode.Read)) 
using (ZipArchive newArchive = 
    new ZipArchive(File.Open(tempFile, FileMode.Create), ZipArchiveMode.Create)) 
{ 
    foreach (ZipArchiveEntry entry in original.Entries) 
    { 
     ZipArchiveEntry newEntry = newArchive.Create(entry.FullName); 

     using (Stream source = entry.Open()) 
     using (Stream destination = newEntry.Open()) 
     { 
      source.CopyTo(destination); 
     } 
    } 

    for (int directoryGroupFileIndex = 0; 
      directoryGroupFileIndex < directoryGroup.Length; 
      directoryGroupFileIndex++) 
    { 
     FileInfo file = directoryGroup[directoryGroupFileIndex]; 
     String archiveEntryName = file.Name; 
     String archiveEntryPath = DateTime.Now.ToString("yyyy-MM-dd"); 
     String archiveEntryFullName = Path.Combine(archiveEntryPath, archiveEntryName); 

     ZipArchiveEntry archiveEntry = newArchive.CreateEntryFromFile(
      file.FullName, archiveEntryFullName, CompressionLevel.Optimal); 
    } 
} 

File.Delete(archiveFileStream); 
File.Move(tempFile, archiveFileStream); 

Обратите внимание, что это на самом деле не собирается быть медленнее, чем ZipArchiveMode.Update. Когда вы используете режим обновления, класс ZipArchive читает весь архив в памяти (как вы отметили), а затем, когда вы его закрываете, он повторно сжимает и записывает все обратно.

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

+0

Привет, спасибо Питер. Это звучит как хорошее решение. Но, похоже, есть и недостаток дизайна. Потому что должно быть возможно добавить некоторые новые записи в архив, не загружая полный файл в память. Как я писал, если вы попробуете, файл на самом деле не перезаписывается. потому что размер просто растет.Во-вторых, файл поврежден. В-третьих, если вы обновите архив (например, с winrar), он перестроит все файлы (старые и новые). Таким образом, я думаю, что ZipArchive расширяет файл, исправляя правильный путь, но пропустить обновление таблицы файлов для включения существующих файлов. –

+2

«файл не перезаписывается» - это потому, что вы не просили перезаписать файл. Вместо этого используйте 'FileMode.Create', и он будет перезаписан. Существует фундаментальная проблема с простое добавление к архиву .zip, который заключается в том, что есть контрольная сумма, которая должна учитывать _all_ данных в файле. По крайней мере, оригинал должен быть обработан, чтобы сделать это правильно; вы не можете просто добавить к существующему файлу. И что такой инструмент, как WinRAR, может восстановить поврежденный файл, ничего не говорит о том, что должен делать 'ZipArchive'; это просто означает, что WinRAR полезен. –

+0

Благодарим за разъяснение. –