web-dev-qa-db-fra.com

Extraire des fichiers d'une archive Zip par programme à l'aide de C # et System.IO.Packaging

J'ai un tas de fichiers Zip qui ont désespérément besoin d'une réorganisation et d'une extraction hiérarchiques. Ce que je peux faire, actuellement, est de créer la structure du répertoire et de déplacer les fichiers Zip vers l'emplacement approprié. Le fromage mystique qui me manque est la partie qui extrait les fichiers de l'archive Zip.

J'ai vu les articles MSDN sur la classe ZipArchive et je les comprends bien. J'ai également vu les façons d'extraire VBScript . Ce n'est pas une classe complexe, donc extraire des trucs devrait être assez simple. En fait, cela fonctionne "principalement". J'ai inclus mon code actuel ci-dessous pour référence.

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

Le problème semble être quelque part dans le GetParts (ou Get Anything d'ailleurs). Il semble que le paquet, bien qu'ouvert, soit vide. En creusant plus profondément, le débogueur montre que le membre privé _zipArchive montre qu'il a en fait des parties. Pièces avec les bons noms et tout. Pourquoi la fonction GetParts ne les récupère-t-elle pas? J'avais essayé de lancer l'ouvert à un ZipArchive et cela n'a pas aidé. Grrr.

47
Craig

Si vous manipulez des fichiers Zip, vous voudrez peut-être consulter une bibliothèque tierce pour vous aider.

Par exemple, DotNetZip, qui a été récemment mis à jour. La version actuelle est maintenant la v1.8. Voici un exemple pour créer un 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");
}

Voici un exemple pour mettre à jour un Zip existant; vous n'avez pas besoin d'extraire les fichiers pour le faire:

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

voici un exemple qui extrait des entrées:

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

DotNetZip prend en charge les caractères multi-octets dans les noms de fichiers, le cryptage Zip, le cryptage AES, les flux, Unicode, les archives auto-extractibles. Fait également Zip64, pour les longueurs de fichier supérieures à 0xFFFFFFFF, ou pour les archives avec plus de 65535 entrées.

gratuit. Open source

obtenez-le à codeplex ou téléchargement direct depuis windows.net - CodePlex a été interrompu et archivé

47
Cheeso

De MSDN ,

Dans cet exemple, la classe Package est utilisée (par opposition au ZipPackage.) Ayant travaillé avec les deux, je n'ai vu la fragilité se produire qu'en cas de corruption dans le fichier Zip. Pas nécessairement une corruption qui lance l'extracteur Windows ou Winzip, mais quelque chose que les composants de packaging ont du mal à gérer.

J'espère que cela vous aidera, peut-être que cela peut vous fournir une alternative au débogage du problème.

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

De " ZipPackage Class " (MSDN):

Bien que les packages soient stockés sous forme de fichiers Zip * via la classe ZipPackage, tous les fichiers Zip ne sont pas des ZipPackages. Un ZipPackage a des exigences spéciales telles que des noms de fichier (partie) conformes à l'URI et un fichier "[Content_Types] .xml" qui définit les types MIME pour tous les fichiers contenus dans le package. La classe ZipPackage ne peut pas être utilisée pour ouvrir des fichiers Zip arbitraires non conformes à la norme Open Packaging Conventions.

Pour plus de détails, voir la section 9.2 "Mappage vers une archive Zip" du standard ECMA International "Open Packaging Conventions", http://www.ecma-international.org/publications/files/ECMA-ST/Office% 20Open% 20XML% 20Part% 202% 20 (DOCX) .Zip (342Kb) ou http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML % 20Part% 202% 20 (PDF) .Zip (1,3 Mo)

* Vous pouvez simplement ajouter ".Zip" à l'extension de n'importe quel fichier basé sur ZipPackage (.docx, .xlsx, .pptx, etc.) pour l'ouvrir dans votre utilitaire Zip préféré.

31
Luke

J'avais exactement le même problème! Pour obtenir la méthode GetParts () pour renvoyer quelque chose, j'ai dû ajouter le fichier [Content_Types] .xml à la racine de l'archive avec un nœud "Default" pour chaque extension de fichier incluse. Une fois que j'ai ajouté cela (en utilisant simplement l'Explorateur Windows), mon code a pu lire et extraire le contenu archivé.

Vous trouverez plus d'informations sur le fichier [Content_Types] .xml ici:

http://msdn.Microsoft.com/en-us/magazine/cc163372.aspx - Il existe un exemple de fichier sous la figure 13 de l'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();
        } 
    } 
} 

Remarque: ce code utilise la méthode Stream.CopyTo dans .NET 4.0

13
Joshua

Je suis d'accord avec le Cheeso. System.IO.Packaging est gênant lors de la manipulation de fichiers Zip génériques, car il a été conçu pour les documents Office Open XML. Je suggère d'utiliser DotNetZip ou SharpZipLib

6
Rad

(Il s'agit essentiellement d'une reformulation de cette réponse )

Il s'avère que System.IO.Packaging.ZipPackage ne prend pas en charge PKZIP, c'est pourquoi lorsque vous ouvrez un fichier Zip "générique", aucune "pièce" n'est retournée. Cette classe ne prend en charge qu'une partie spécifique des fichiers Zip (voir les commentaires au bas de description MSDN ) utilisés entre autres en tant que packages de services Windows Azure jusqu'au SDK 1.6 - c'est pourquoi si vous décompressez un package de services, puis remballez-le en utilisant par exemple le pack Info-Zip, il deviendra invalide.

1
sharptooth