wake-up-neo.net

Programmgesteuertes Extrahieren von Dateien aus einem Zip-Archiv mit C # und System.IO.Packaging

Ich habe eine Reihe von Zip-Dateien, die dringend einer hierarchischen Reorganisation und Extraktion bedürfen. Derzeit kann ich die Verzeichnisstruktur erstellen und die Zip-Dateien an den richtigen Speicherort verschieben. Der mystische Käse, den ich vermisse, ist der Teil, der die Dateien aus dem Zip-Archiv extrahiert.

Ich habe die MSDN-Artikel in der Klasse ZipArchive gesehen und verstehe sie angemessen. Ich habe auch die VBScript-Möglichkeiten zum Extrahieren gesehen. Dies ist keine komplexe Klasse, daher sollte das Extrahieren von Inhalten ziemlich einfach sein. Tatsächlich funktioniert es "meistens". Ich habe meinen aktuellen Code unten als Referenz angegeben.

 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.
    }
 }

Das Problem scheint irgendwo in der GetParts (oder Get Anything für diese Angelegenheit) zu liegen. Offenbar ist die Packung leer. Wenn Sie den Debugger vertiefen, wird angezeigt, dass das private Mitglied _zipArchive tatsächlich Teile enthält. Teile mit den richtigen Namen und allem. Warum ruft die Funktion GetParts sie nicht ab? Ich habe versucht, das Open in ein ZipArchive zu werfen, und das hat nicht geholfen. Grrr.

47
Craig

Wenn Sie Zip-Dateien bearbeiten, sollten Sie sich eine Bibliothek eines Drittanbieters ansehen, um Hilfe zu erhalten.

Zum Beispiel DotNetZip, das kürzlich aktualisiert wurde. Die aktuelle Version ist jetzt v1.8. Hier ist ein Beispiel zum Erstellen einer Postleitzahl:

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

Hier ist ein Beispiel zum Aktualisieren einer vorhandenen Zip; Sie müssen die Dateien nicht extrahieren, um dies zu tun:

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

hier ist ein Beispiel, das Einträge extrahiert:

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

DotNetZip unterstützt Multi-Byte-Zeichen in Dateinamen, Zip-Verschlüsselung, AES-Verschlüsselung, Streams, Unicode und selbstextrahierenden Archiven. Funktioniert auch mit Zip64 für Dateien mit einer Länge von mehr als 0xFFFFFFFF oder für Archive mit mehr als 65535 Einträgen.

kostenlos. Open Source

holen Sie es sich unter Codeplex oder direkter Download von windows.net - CodePlex wurde eingestellt und archiviert

47
Cheeso

From MSDN ,

In diesem Beispiel wird die Package-Klasse verwendet (im Gegensatz zum ZipPackage). Nachdem ich mit beiden gearbeitet habe, habe ich nur Probleme festgestellt, wenn die Zip-Datei beschädigt ist. Dies ist nicht unbedingt eine Beschädigung, die den Windows-Extraktor oder Winzip auslöst, sondern ein Problem, das die Verpackungskomponenten bei der Verarbeitung haben.

Ich hoffe, dies hilft, vielleicht bietet es Ihnen eine Alternative zum Debuggen des Problems.

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

Ich hatte genau das gleiche Problem! Damit die GetParts () -Methode etwas zurückgibt, musste ich die Datei [Content_Types] .xml mit einem "Standard" -Knoten für jede enthaltene Dateierweiterung zum Stammverzeichnis des Archivs hinzufügen. Nachdem ich dies hinzugefügt hatte (nur mit Windows Explorer), konnte mein Code den archivierten Inhalt lesen und extrahieren.

Weitere Informationen zur Datei [Content_Types] .xml finden Sie hier:

http://msdn.Microsoft.com/en-us/magazine/cc163372.aspx - Unter Abbildung 13 des Artikels befindet sich eine Beispieldatei.

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

Hinweis: In diesem Code wird die Stream.CopyTo-Methode in .NET 4.0 verwendet

13
Joshua

Ich stimme mit Cheeso überein. System.IO.Packaging ist im Umgang mit generischen Zip-Dateien umständlich, da es für Office Open XML-Dokumente entwickelt wurde. Ich würde vorschlagen, DotNetZip oder SharpZipLib zu verwenden

6
Rad

(Dies ist im Grunde eine Umformulierung von diese Antwort )

Stellt sich heraus, dass System.IO.Packaging.ZipPackage unterstützt PKZIP nicht, daher werden beim Öffnen einer "generischen" Zip-Datei keine "Teile" zurückgegeben. Diese Klasse unterstützt nur eine bestimmte Variante von Zip-Dateien (siehe Kommentare am Ende von MSDN-Beschreibung ), die unter anderem als Windows Azure-Servicepakete bis SDK 1.6 verwendet werden. Deshalb, wenn Sie ein Servicepaket entpacken und dann Packe es mit dem Info-Zip Packer neu, es wird ungültig.

1
sharptooth