SharpCompress& LZMA2 7z存档 - 特定文件的提取速度非常慢。为什么?替代方案?

时间:2017-04-25 09:14:05

标签: c# performance compression lzma sharpcompress

我有一个带有LZMA2压缩的7zip存档(压缩级别:超级)。 该档案包含1,749个文件,总共最初的大小为661mb。 压缩文件大小为39mb。

现在我正在尝试使用C#从此存档中提取一个小的(~200kb'ish)单个文件。

我从IArchive获得相应的IArchiveEntry(工作速度相对较快), 但是然后调用IArchiveEntry.WriteToFile(targetPath)需要大约33秒!如果我写一个内存流而不是类似的话。 (编辑:当我在压缩级别=正常的7z LZMA2存档上运行时,它仍然需要9秒)

当我在实际的7zip应用程序中打开相同的存档并从那里提取相同的文件时,它只需要大约2-3秒。 我怀疑它是某种多核(7zip)vs单核(SharpCompress可能?)的东西,但我没有注意到7zip解压缩过程中的CPU使用率飙升......可能它太快而不能引人注意......

有没有人知道SharpCompress这么慢的速度会出现什么问题?我可能错过了一些设置或使用了错误的工厂(ArchiveFactory)吗?

如果没有 - 是否有任何C#库在解压缩时可能明显更快?

作为参考,这里是我如何使用SharpCompress提取的草图:

private void Extract()
    {
        using(var archive = GetArchive())
        {
          var entryPath = /* ... path to entry .. */
          var entry = TryGetEntry(archive, entryPath);
          entry.WriteToFile(some_target_path);
        }
    }


    private IArchive GetArchive()
    {
        string path = /* .. path to my .7z file */;
        return ArchiveFactory.Open(path);
    }

    private IArchiveEntry TryGetEntry(IArchive archive, string path)
    {
        path = path.Replace("\\", "/");

        foreach (var entry in archive.Entries)
        {
            if (!entry.IsDirectory)
            {
                if (entry.Key == path)
                    return entry;
            }
        }

        return null;
    }

更新:对于临时解决方案,我现在在我的应用程序中包含7zip SDK中的7zr.exe,并在新进程中运行它以提取单个文件,将进程的输出读入二进制流。 与SharpCompress的〜33秒相比,这可以在约3秒内工作。现在工作,但有点丑..所以仍然好奇为什么SharpCompress似乎那么慢

2 个答案:

答案 0 :(得分:4)

这一行是问题

foreach (var entry in archive.Entries)

问题描述here(即如果有100个文件,它会解压缩第一个文件100次,第二个文件解压缩99次,依此类推)

您需要使用阅读器(仅向前)。请参阅API 但那里的示例代码不支持7z。

对于7z,您可以使用archive.ExtractAllEntries(),例如

var reader = archive.ExtractAllEntries();
while (reader.MoveToNextEntry())
{
    if (!reader.Entry.IsDirectory)
        reader.WriteEntryToDirectory(extractDir, new ExtractionOptions() { ExtractFullPath = false, Overwrite = true });
}

会快得多。

答案 1 :(得分:-1)

如果您需要所有文件,也可以这样做:

using var reader = archive.ExtractAllEntries();
reader.WriteAllToDirectory(targetPath, new ExtractionOptions() { ExtractFullPath = true, Overwrite = true });