在 .NET 中以编程方式解压文件

271

我正在尝试编写程序来解压缩压缩文件。

我尝试使用.NET中的System.IO.Compression.GZipStream类,但当我的应用程序运行(实际上是单元测试)时,我会收到此异常:

System.IO.InvalidDataException: GZip头中的魔术数字不正确。 确保您传递了GZip流。

我现在意识到一个.zip文件不同于一个.gz文件,并且GZipZip也不同。

然而,由于我能够通过手动双击压缩文件并单击“提取所有文件”按钮来提取文件,因此我认为也应该有一种在代码中实现这个操作的方法。

因此,我尝试使用Process.Start()作为输入参数,将路径设置为压缩文件的路径。这会导致我的应用程序打开一个窗口,显示压缩文件中的内容。这很好,但是该应用程序将被安装在没有人点击“提取所有文件”按钮的服务器上。

那么,如何让我的应用程序提取压缩文件中的文件?

或者还有其他方法吗? 我更喜欢在代码中完成,而不需要下载任何第三方库或应用程序; 安全部门对此并不太感冒...


17
你的安全部门更愿意你自己编写代码,而不是使用已经被调试和审查过的库?你可以同时使用库和“自己编写代码”(获取源代码并自行编译),但我认为重新发明轮子比使用经过验证的库带来的任何安全问题都更严重。 - Jared Updike
11
@Jared - 当管理层有了一个想法… - Steven Evers
5
如果您使用第三方产品,安全部门的风险会更小。只需下载dotnetzip并将其重命名为“[插入公司名称].ziplibrary.dll”。 - Simon
15个回答

569

使用.NET 4.5,你现在可以使用.NET框架来解压文件:

using System;
using System.IO;

namespace ConsoleApplication
{
  class Program
  {
    static void Main(string[] args)
    {
      string startPath = @"c:\example\start";
      string zipPath = @"c:\example\result.zip";
      string extractPath = @"c:\example\extract";

      System.IO.Compression.ZipFile.CreateFromDirectory(startPath, zipPath);
      System.IO.Compression.ZipFile.ExtractToDirectory(zipPath, extractPath);
    }
  }
}

上面的代码直接来自于Microsoft的文档:http://msdn.microsoft.com/en-us/library/ms404280(v=vs.110).aspx

ZipFile包含在System.IO.Compression.FileSystem程序集中。(感谢nateirvin...请参见下面的评论)。您需要向框架程序集System.IO.Compression.FileSystem.dll添加一个DLL引用。


134
顺便提一句,ZipFile 包含在程序集 System.IO.Compression.FileSystem 中。 - nateirvin
79
这意味着您需要向框架程序集“System.IO.Compression.FileSystem.dll”添加一个DLL引用。 - Chris Schiffhauer
那RAR文件怎么办?上面的代码无法提取RAR文件。 - Raghu
2
我在我的asp.net core web api中尝试了这个,它可以正常读取第一个条目,但是在第二个条目上总是会出现错误“本地文件头已损坏”。你对此有什么想法吗? - SoftSan
1
@Aidan,这很好知道,不过如果可能的话,在Mac/Linux上最好不要使用.NET。在无法避免的情况下,这是有帮助的。谢谢。 - bsara
显示剩余3条评论

137

针对 .Net 4.5+ 版本

并不总是希望将未压缩的文件写入磁盘。作为 ASP.Net 开发人员,我必须调整权限以授予应用程序在文件系统中写入的权限。通过在内存中使用流,我可以避开所有这些问题并直接读取文件:

using (ZipArchive archive = new ZipArchive(postedZipStream))
{
    foreach (ZipArchiveEntry entry in archive.Entries)
    {
         var stream = entry.Open();
         //Do awesome stream stuff!!
    }
}

或者,您仍然可以通过调用 ExtractToFile() 将解压缩的文件写入磁盘:

using (ZipArchive archive = ZipFile.OpenRead(pathToZip))
{
    foreach (ZipArchiveEntry entry in archive.Entries)
    {
        entry.ExtractToFile(Path.Combine(destination, entry.FullName));
    }
} 

使用ZipArchive类,您需要将引用添加到System.IO.Compression命名空间和System.IO.Compression.FileSystem


11
微软真的要到4.5+版本才添加了本地解压器吗? - JWP
2
@JohnPeters GZipStream在.Net 2.0中被重新添加(https://msdn.microsoft.com/en-us/library/system.io.compression.gzipstream(v=vs.80).aspx)。然而,在内存中处理多个文件的归档并不容易。新的`ZipArchive`对象非常适合这种情况。 - Mister Epic
2
这是一个特别好的选择,因为它允许解压缩而不使用文件系统(在我的情况下,我正在使用嵌入式资源),而且它也不是第三方扩展。 - ANeves
4
当我可以直接使用 ZipFile.ExtractToDirectory(inputFile, outputDir); 时,为什么应该使用 foreach 循环来执行 ExtractToFile 操作?第一种方法的优势是什么? - The Fluffy Robot
1
在.NET 4.6.1中,我无法从'System.IO.Compression.FileSystem'获取'ZipArchive',有什么想法吗? - Ravi Anand
显示剩余5条评论

66

我们在许多项目中成功地使用了SharpZipLib。我知道它是第三方工具,但源代码已经包含,如果您选择在这里重新发明轮子,可以提供一些见解。


3
我尝试使用SharpZipLib,它很好用。我想我需要看看是否禁止使用第三方库和应用程序是一项严格的规则还是更像是一条指导方针。 - Petteri
11
我不了解你们公司的情况,但我的经验一直是,如果你撰写一个“商业案例”的描述来说明为什么需要这种例外,那么就有可能获得这种规则的例外。指出与自己制作相比的成本节约以及源代码可以被检查的事实。作为备选方案,即使他们不允许你使用dll文件,你通常也可以获得使用源代码的许可,然后自行编译它(或者至少编译你实际需要使用的部分...)。 - RolandTumble
您不必使用外部库来解压缩zip文件,可以使用System32中的Shell32。请参见https://dev59.com/OXLYa4cB1Zd3GeqPZpqL#43066281。 - arturn
1
代码示例:https://dev59.com/W2Eh5IYBdhLWcg3wZy3H#22444096 - Glebka

60

免费,没有外部DLL文件。所有内容都在一个CS文件中。一个下载只包含一个CS文件,另一个下载则是一个易于理解的示例。今天刚试用了一下,我简直不敢相信设置是如此简单。第一次尝试就成功了,没有错误,没有任何问题。

https://github.com/jaime-olivares/zipstorer


说话太早了!我想立即从http下载流中解压文件。这不起作用,因为它在流上使用了Seek操作 :( 好吧,多亏了源代码,现在我可以编写自己的ZipStream了... - oyophant
我的问题的最佳解决方案,因为我正在编写一个更新程序,无法在提取过程中涉及任何DLL,否则我也必须更新它们...这很好。谢谢! - Niklas
答案值得100点赏金。依赖性很小,最兼容且易于学习。 - Gray Programmerz

28

使用DotNetZip库,网址为http://www.codeplex.com/DotNetZip,该库是一个类库和工具集,用于操作zip文件。可以使用VB、C#或任何.NET语言来轻松创建、提取或更新zip文件...

DotNetZip适用于拥有完整.NET Framework的PC,也适用于使用.NET Compact Framework的移动设备。可以在VB、C#或任何.NET语言或任何脚本环境中创建和读取zip文件...

如果您只想要更好的DeflateStream或GZipStream类来替换.NET BCL内置的类,则DotNetZip也提供了这种功能。DotNetZip的DeflateStream和GZipStream可作为单独的程序集使用,基于Zlib的.NET端口。这些流支持压缩级别,并且比内置类提供更好的性能。还有一个ZlibStream来完成整个设置(RFC 1950、1951、1952)...


2
嗯...但那是第三方库! - Petteri
31
非常敏锐的你。除非你想花费数月时间来实现自己的Zip文件阅读器,否则这是你最好的选择。 - Sam Axe
这个比SharpZipLib好得多。 - Kugel
5
你在问我一个将近5年的答案,建议你自己进行一些研究,相信你会找到答案。 - Sam Axe
2
@PhilCooper 这是一个非常古老的问题,我建议使用内置的System.IO.Compression.ZipFile。根据我在生产数千个zip文件时的经验,我IRC以前对SharpZipLib有非常糟糕的体验。 - Kugel
显示剩余3条评论

12
String ZipPath = @"c:\my\data.zip";
String extractPath = @"d:\\myunzips";
ZipFile.ExtractToDirectory(ZipPath, extractPath);

要使用ZipFile类,您必须在项目中添加对System.IO.Compression.FileSystem程序集的引用


1
源代码:https://msdn.microsoft.com/zh-cn/library/system.io.compression.zipfile(v=vs.110).aspx - gkubed

4
这将完成它:System.IO.Compression.ZipFile.ExtractToDirectory(ZipName, ExtractToPath)

2

我用这个工具来压缩或解压多个文件。正则表达式的内容不是必需的,但我用它来更改日期标记并删除不需要的下划线。我在“Compress >> zipPath”字符串中使用空字符串来为所有文件添加前缀(如果需要)。另外,根据我的操作,我通常注释掉Compress()或Decompress()之一。

using System;
using System.IO.Compression;
using System.IO;
using System.Text.RegularExpressions;

namespace ZipAndUnzip
{
    class Program
    {
        static void Main(string[] args)
        {
            var directoryPath = new DirectoryInfo(@"C:\your_path\");

            Compress(directoryPath);
            Decompress(directoryPath);
        }

        public static void Compress(DirectoryInfo directoryPath)
        {
            foreach (DirectoryInfo directory in directoryPath.GetDirectories())
            {
                var path = directoryPath.FullName;
                var newArchiveName = Regex.Replace(directory.Name, "[0-9]{8}", "20130913");
                newArchiveName = Regex.Replace(newArchiveName, "[_]+", "_");
                string startPath = path + directory.Name;
                string zipPath = path + "" + newArchiveName + ".zip";

                ZipFile.CreateFromDirectory(startPath, zipPath);
            }

        }

        public static void Decompress(DirectoryInfo directoryPath)
        {
            foreach (FileInfo file in directoryPath.GetFiles())
            {
                var path = directoryPath.FullName;
                string zipPath = path + file.Name;
                string extractPath = Regex.Replace(path + file.Name, ".zip", "");

                ZipFile.ExtractToDirectory(zipPath, extractPath);
            }
        }


    }
}

1
这需要 dot net 4.5 - 只是一个注意,因为其他人回答时提到了 ZipFile,而我仍在使用 3.5。 - Thronk

2
你可以使用DeflateStream在.NET 3.5中完成所有操作。但是,.NET 3.5缺少处理文件头部分的能力,这些部分用于组织压缩文件。PKWare已经发布了这些信息,您可以使用它来在创建所需结构后处理zip文件。这不是特别困难的,而且是在没有使用第三方代码的情况下进行工具构建的良好实践。
虽然这不是一个一行的答案,但如果你愿意并且有时间,完全可以做到。我在几个小时内编写了一个类来完成这项工作,并从中获得了仅使用.NET 3.5压缩和解压缩文件的能力。

2

标准的zip文件通常使用deflate算法。

如果不使用第三方库提取文件,请使用DeflateStream。您需要更多关于zip文件存档格式的信息,因为Microsoft只提供压缩算法。

您也可以尝试使用zipfldr.dll。这是Microsoft的压缩库(从“发送到”菜单中的压缩文件)。它似乎是一个com库,但没有文档说明。通过实验,您可能能够使其正常工作。


我正在尝试使用DeflateStream类。这次我遇到了System.IO.InvalidDataException:块长度与其补码不匹配。 - Petteri
正如我之前所说,微软只提供了算法。你还需要了解zip存档格式的信息。http://en.wikipedia.org/wiki/ZIP_(file_format)可以帮助你入门。请查看页面底部的参考链接以获取更详细的信息。 - Kenneth Cochran
2
我也偶然发现了.NET 3.5中的System.IO.Packaging.Package。虽然它不是很直观,但看起来它可能会解决问题。 - Kenneth Cochran

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接