c# - System.IO.Compression.ZipArchive 内存管理

标签 c# zip compression .net-4.5

在 .Net 4.5 中,System.IO.Compression.ZipArchive 类得到一些更新。

如此处可读 (http://msdn.microsoft.com/en-us/magazine/jj133817.aspx),它现在应该执行“典型操作不需要将整个存档读入内存”。

为了测试,我尝试压缩 10 个文件,每个文件大小为 200MB。

如果您使用此代码创建新的 zip 存档,这会很好用(整个过程的内存使用率较低):

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”,它会按预期工作,但仅限于小文件。 像我这样的文件会抛出内存不足异常,因为完整的存档已加载到内存中。

我现在的问题是:我做错了吗,这是一个错误还是设计缺陷?

最佳答案

您编写的代码导致 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 类会将整个存档读入内存(如您所述),然后当您关闭它时,它会重新压缩并将所有内容写回。

上面的计算基本上完全相同,但只是使用磁盘作为中间存储而不是内存。

关于c# - System.IO.Compression.ZipArchive 内存管理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27446495/

相关文章:

c# - 替代if,else if

java - 将已压缩的文件插入 zip 文件中

java - 如何在另一个 zip 中创建 zip。我得到了 fileNotFoundException

c# - SendToZip 和 C# CreateFromDirectory zipfile 之间的区别

c# - 在重定向时保持 HTTP 基本身份验证有效

c# - String 、 Int32 等的操作在哪里定义?

compression - 添加 permessage-deflate 响应 header 后,websocket 帧是否会自动压缩?

hadoop - 错误 : Could not initialize class org. xerial.snappy.Snappy

php - 如何将 6+31 个数字字符塞入 22 个字母数字字符?

c# - 使用 C# 快速获取 Active Directory 中组成员列表的方法