Extracting files from a Zip archive programmatically using C# and System.IO.Packaging

一个人想着一个人 提交于 2019-11-28 04:18:41
Cheeso

If you are manipulating ZIP files, you may want to look into a 3rd-party library to help you.

For example, DotNetZip, which has been recently updated. The current version is now v1.8. Here's an example to create a zip:

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");
}

Here's an example to update an existing zip; you don't need to extract the files to do it:

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();
}

here's an example that extracts entries:

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

DotNetZip supports multi-byte chars in filenames, Zip encryption, AES encryption, streams, Unicode, self-extracting archives. Also does ZIP64, for file lengths greater than 0xFFFFFFFF, or for archives with more than 65535 entries.

free. open source

get it at codeplex or direct download from windows.net - CodePlex has been discontinued and archived

From MSDN,

In this sample, the Package class is used (as opposed to the ZipPackage.) Having worked with both, I've only seen flakiness happen when there's corruption in the zip file. Not necessarily corruption that throws the Windows extractor or Winzip, but something that the Packaging components have trouble handling.

Hope this helps, maybe it can provide you an alternative to debugging the issue.

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();
    }
}
Luke

From "ZipPackage Class" (MSDN):

While Packages are stored as Zip files* through the ZipPackage class, all Zip files are not ZipPackages. A ZipPackage has special requirements such as URI-compliant file (part) names and a "[Content_Types].xml" file that defines the MIME types for all the files contained in the Package. The ZipPackage class cannot be used to open arbitary Zip files that do not conform to the Open Packaging Conventions standard.

For further details see Section 9.2 "Mapping to a ZIP Archive" of the ECMA International "Open Packaging Conventions" standard, http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%20Part%202%20(DOCX).zip (342Kb) or http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%20Part%202%20(PDF).zip (1.3Mb)

*You can simply add ".zip" to the extension of any ZipPackage-based file (.docx, .xlsx, .pptx, etc.) to open it in your favorite Zip utility.

Joshua

I was having the exact same problem! To get the GetParts() method to return something, I had to add the [Content_Types].xml file to the root of the archive with a "Default" node for every file extension included. Once I added this (just using Windows Explorer), my code was able to read and extract the archived contents.

More information on the [Content_Types].xml file can be found here:

http://msdn.microsoft.com/en-us/magazine/cc163372.aspx - There is an example file below Figure 13 of the article.

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();
        } 
    } 
} 

Note: this code uses the Stream.CopyTo method in .NET 4.0

Rad

I agree withe Cheeso. System.IO.Packaging is awkward when handling generic zip files, seeing as it was designed for Office Open XML documents. I'd suggest using DotNetZip or SharpZipLib

sharptooth

(This is basically a rephrasing of this answer)

Turns out that System.IO.Packaging.ZipPackage doesn't support PKZIP, that's why when you open a "generic" ZIP file no "parts" are returned. This class only supports some specific flavor of ZIP files (see comments at the bottom of MSDN description) used among other as Windows Azure service packages up to SDK 1.6 - that's why if you unpack a service package and then repack it using say Info-ZIP packer it will become invalid.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!