将文件添加到现有Zip中 - 性能问题

时间:2015-05-13 18:46:11

标签: c# performance wcf dotnetzip

我有一个WCF Web服务,可以将文件保存到一个文件夹(大约200,000个小文件)。 之后,我需要将它们移动到另一台服务器。

我发现的解决方案是拉链然后移动它们。

当我采用这个解决方案时,我已经用(20,000个文件)进行了测试,压缩20,000个文件只需要大约2分钟,并且移动zip非常快。 但在生产中,压缩200,000个文件需要2个多小时。

这是压缩文件夹的代码:

using (ZipFile zipFile = new ZipFile())
{
    zipFile.UseZip64WhenSaving = Zip64Option.Always;
    zipFile.CompressionLevel = CompressionLevel.None;
    zipFile.AddDirectory(this.SourceDirectory.FullName, string.Empty);

    zipFile.Save(DestinationCurrentFileInfo.FullName);
}

我想修改WCF网络服务,这样就不会保存到文件夹,而是保存到zip。

我使用以下代码进行测试:

var listAes = Directory.EnumerateFiles(myFolder, "*.*", SearchOption.AllDirectories).Where(s => s.EndsWith(".aes")).Select(f => new FileInfo(f));

foreach (var additionFile in listAes)
{
    using (var zip = ZipFile.Read(nameOfExistingZip))
    {
        zip.CompressionLevel = Ionic.Zlib.CompressionLevel.None;
        zip.AddFile(additionFile.FullName);

        zip.Save();
    }

    file.WriteLine("Delay for adding a file  : " + sw.Elapsed.TotalMilliseconds);
    sw.Restart();
}

要添加到zip的第一个文件只需要5毫秒,但要添加的第10,000个文件需要800毫秒。

有没有办法优化这个?或者如果您有其他建议?

修改

上面显示的示例仅用于测试,在WCF Web服务中,我将有不同的请求发送我需要添加到Zip文件的文件。 由于WCF是无规则的,每次调用我都会有一个新类的实例,那么如何保持Zip文件打开以添加更多文件?

4 个答案:

答案 0 :(得分:3)

我可以看到您只想将200,000个文件分组到一个大的单个文件中,不压缩(如tar存档)。 要探索的两个想法:

  1. 尝试使用除Zip以外的其他文件格式,因为它可能不是最快的。 Tar(磁带存档)浮现在脑海中(由于其简单性而具有自然speed advantages),它甚至具有append mode,这正是您确保O(1)操作所需的。 SharpCompress是一个允许您使用此格式(以及其他格式)的库。

  2. 如果您可以控制您的远程服务器,您可以实现自己的文件格式,我能想到的最简单的方法是分别压缩每个新文件(以存储文件元数据,如名称,日期等) 。在文件内容本身中),然后将每个这样的压缩文件附加到单个原始字节文件中。您只需要存储字节偏移量(由另一个txt文件中的列分隔)以允许远程服务器将大文件拆分为200,000个压缩文件,然后解压缩每个文件以获取元数据。我想这也是焦油在场景背后的作用:)。

  3. 您是否尝试过压缩到MemoryStream而不是文件,只在当天完成时才刷新文件?当然,为了备份,您的WCF服务必须保留所接收的单个文件的副本,直到您确定它们已“提交”到远程服务器。

  4. 如果你确实需要压缩,7-Zip(和摆弄选项)非常值得一试。

答案 1 :(得分:0)

您正在重复打开文件,为什么不添加循环并将它们全部添加到一个zip,然后保存?

var listAes = Directory.EnumerateFiles(myFolder, "*.*", SearchOption.AllDirectories)
    .Where(s => s.EndsWith(".aes"))
    .Select(f => new FileInfo(f));

using (var zip = ZipFile.Read(nameOfExistingZip))
{
    foreach (var additionFile in listAes)
    {
        zip.CompressionLevel = Ionic.Zlib.CompressionLevel.None;
        zip.AddFile(additionFile.FullName);
    }
    zip.Save();
}

如果文件并非全部可用,您至少可以将它们一起批处理。因此,如果您预计有200,000个文件,但到目前为止您只收到10个文件,请不要打开zip,添加一个,然后关闭它。等待几个进来并分批添加。

答案 2 :(得分:0)

如果您对100 * 20,000个文件的性能表现不错,那么您不能简单地将大型ZIP分区为100" small" ZIP文件?为简单起见,每分钟创建一个新的ZIP文件,并在名称中加上时间戳。

答案 3 :(得分:-1)

您可以使用.Net TPL(任务并行库)压缩所有文件,如下所示:

    while(0 != (read = sourceStream.Read(bufferRead, 0, sliceBytes)))
{
   tasks[taskCounter] = Task.Factory.StartNew(() => 
     CompressStreamP(bufferRead, read, taskCounter, ref listOfMemStream, eventSignal)); // Line 1
   eventSignal.WaitOne(-1);           // Line 2
   taskCounter++;                     // Line 3
   bufferRead = new byte[sliceBytes]; // Line 4
}

Task.WaitAll(tasks);                  // Line 6

这里有一个已编译的库和源代码:

http://www.codeproject.com/Articles/49264/Parallel-fast-compression-unleashing-the-power-of