使用C#和System.IO.Packaging以编程方式从Zip存档中提取文件

我有一堆ZIP文件,迫切需要一些分层重组和提取。 目前,我可以做的是创build目录结构并将zip文件移动到适当的位置。 我缺less的神秘的奶酪是从ZIP压缩文件中提取文件的部分。

我已经看到了ZipArchive类的MSDN文章,并理解它们的合理性。 我也看到了VBScript的方法来提取 。 这不是一个复杂的类,所以提取的东西应该很简单。 事实上,它“大部分”起作用。 我已经包含了我现在的代码以供参考。

  using (ZipPackage package = (ZipPackage)Package.Open(@"..\..\test.zip", FileMode.Open, FileAccess.Read)) { PackagePartCollection packageParts = package.GetParts(); foreach (PackageRelationship relation in packageParts) { //Do Stuff but never gets here since packageParts is empty. } } 

这个问题似乎在GetParts某个地方(或者为此而获取任何东西 )。 看来这个软件包在打开时是空的。 深入挖掘debugging器显示私有成员_zipArchive显示它实际上有部分。 有正确的名字和一切的部分。 为什么GetParts函数不能检索它们? 我试着把开放的ZipArchive放在一起,这没有帮助。 哎呀。

如果您正在操作ZIP文件,您可能需要查看第三方库来帮助您。

例如,最近更新的DotNetZip。 目前的版本现在是v1.8。 这是一个创buildzip的例子:

 using (ZipFile zip = new ZipFile()) { zip.AddFile("c:\\photos\\personal\\7440-N49th.png"); zip.AddFile("c:\\Desktop\\2005_Annual_Report.pdf"); zip.AddFile("ReadMe.txt"); zip.Save("Archive.zip"); } 

以下是更新现有压缩文件的示例。 你不需要提取文件来做到这一点:

 using (ZipFile zip = ZipFile.Read("ExistingArchive.zip")) { // 1. remove an entry, given the name zip.RemoveEntry("README.txt"); // 2. Update an existing entry, with content from the filesystem zip.UpdateItem("Portfolio.doc"); // 3. modify the filename of an existing entry // (rename it and move it to a sub directory) ZipEntry e = zip["Table1.jpg"]; e.FileName = "images/Figure1.jpg"; // 4. insert or modify the comment on the zip archive zip.Comment = "This zip archive was updated " + System.DateTime.ToString("G"); // 5. finally, save the modified archive zip.Save(); } 

这是一个提取条目的例子:

 using (ZipFile zip = ZipFile.Read("ExistingZipFile.zip")) { foreach (ZipEntry e in zip) { e.Extract(TargetDirectory, true); // true => overwrite existing files } } 

DotNetZip支持文件名,Zipencryption,AESencryption,stream,Unicode,自解压档案等多字节字符。 对于大于0xFFFFFFFF的文件长度,或对于具有多于65535个条目的存档,也是ZIP64。

自由。 开源

在codeplex上得到它

从MSDN ,

在这个示例中,使用了Package类(与ZipPackage相对)。在使用这两个函数之后,我只看到zip文件损坏时发生了轻微的变化。 不一定会引起Windows提取程序或Winzip的损坏,但包装组件在处理时遇到问题。

希望这有助于,也许它可以为您提供一个替代debugging问题。

 using System; using System.IO; using System.IO.Packaging; using System.Text; class ExtractPackagedImages { static void Main(string[] paths) { foreach (string path in paths) { using (Package package = Package.Open( path, FileMode.Open, FileAccess.Read)) { DirectoryInfo dir = Directory.CreateDirectory(path + " Images"); foreach (PackagePart part in package.GetParts()) { if (part.ContentType.ToLowerInvariant().StartsWith("image/")) { string target = Path.Combine( dir.FullName, CreateFilenameFromUri(part.Uri)); using (Stream source = part.GetStream( FileMode.Open, FileAccess.Read)) using (Stream destination = File.OpenWrite(target)) { byte[] buffer = new byte[0x1000]; int read; while ((read = source.Read(buffer, 0, buffer.Length)) > 0) { destination.Write(buffer, 0, read); } } Console.WriteLine("Extracted {0}", target); } } } } Console.WriteLine("Done"); } private static string CreateFilenameFromUri(Uri uri) { char [] invalidChars = Path.GetInvalidFileNameChars(); StringBuilder sb = new StringBuilder(uri.OriginalString.Length); foreach (char c in uri.OriginalString) { sb.Append(Array.IndexOf(invalidChars, c) < 0 ? c : '_'); } return sb.ToString(); } } 

来自“ ZipPackage Class ”(MSDN):

当包通过ZipPackage类存储为Zip文件*时,所有Zip文件都不是ZipPackages。 ZipPackage具有特殊的要求,例如符合URI的文件(部分)名称和定义包中包含的所有文件的MIMEtypes的“[Content_Types] .xml”文件。 ZipPackage类不能用于打开不符合开放包装惯例标准的任意Zip文件。

有关详细信息,请参阅ECMA国际“开放包装协定”标准的第9.2节“映射到ZIP档案”, http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML% 20Part%202%20(DOCX).zip (342Kb)或http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%20Part%202%20(PDF).zip (1.3MB)

*您可以简单地将“.zip”添加到任何基于ZipPackage的文件(.docx,.xlsx,.pptx等)的扩展名中,以在您喜欢的Zip实用程序中打开它。

我遇到了完全相同的问题! 为了让GetParts()方法返回一些内容,我必须将[Content_Types] .xml文件添加到归档的根目录,每个文件扩展名都包含一个“Default”节点。 一旦我添加了这个(只使用Windows资源pipe理器),我的代码就可以读取和提取存档的内容。

有关[Content_Types] .xml文件的更多信息可以在这里find:

http://msdn.microsoft.com/en-us/magazine/cc163372.aspx – 文章的图13下方有一个示例文件。

 var zipFilePath = "c:\\myfile.zip"; var tempFolderPath = "c:\\unzipped"; using (Package package = ZipPackage.Open(zipFilePath, FileMode.Open, FileAccess.Read)) { foreach (PackagePart part in package.GetParts()) { var target = Path.GetFullPath(Path.Combine(tempFolderPath, part.Uri.OriginalString.TrimStart('/'))); var targetDir = target.Remove(target.LastIndexOf('\\')); if (!Directory.Exists(targetDir)) Directory.CreateDirectory(targetDir); using (Stream source = part.GetStream(FileMode.Open, FileAccess.Read)) { FileStream targetFile = File.OpenWrite(target); source.CopyTo(targetFile); targetFile.Close(); } } } 

注意:此代码使用.NET 4.0中的Stream.CopyTo方法

我和Cheeso一致。 System.IO.Packaging在处理通用zip文件时很笨拙,因为它是为Office Open XML文档devise的。 我build议使用DotNetZip或SharpZipLib

(这基本上是这个答案的改写)

事实certificate, System.IO.Packaging.ZipPackage不支持PKZIP,这就是为什么当你打开一个“通用的”ZIP文件时,不会返回“parts”。 这个类只支持一些特定的ZIP文件(见MSDN描述底部的注释 ),作为Windows Azure服务包,直到SDK 1.6 – 这就是为什么如果你打开一个服务包,然后用Info-ZIP重新包装它打包机将无效。