2009-02-03 16 views
46

Tengo un montón de archivos ZIP que necesitan una reorganización y extracción jerárquica desesperada. Lo que puedo hacer, actualmente, es crear la estructura del directorio y mover los archivos zip a la ubicación correcta. El queso místico que me falta es la parte que extrae los archivos del archivo ZIP.Extraer archivos de un archivo comprimido mediante programación mediante C# y System.IO.Packaging

He visto los artículos de MSDN en la clase ZipArchive y los entiendo razonablemente bien. También he visto el VBScript ways to extract. Esta no es una clase compleja, por lo que extraer cosas debería ser bastante simple. De hecho, funciona "principalmente". He incluido mi código actual a continuación para referencia.

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

El problema parece estar en algún lugar del GetParts (o Obtener Cualquier cosa para el caso). Parece que el paquete, mientras está abierto, está vacío. Profundizando más, el depurador muestra que el miembro privado _zipArchive muestra que en realidad tiene partes. Partes con los nombres correctos y todo. ¿Por qué la función GetParts no los recuperará? Intenté abrir el archivo ZipArchive y eso no ayudó. Grrr.

+1

FYI, he publicado una solicitud en MS Connect para agregar compatibilidad con el archivo ZIP genérico. También puede votar en https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=477393 –

Respuesta

45

Si está manipulando archivos ZIP, es posible que desee buscar en una biblioteca de terceros para que lo ayude.

Por ejemplo, DotNetZip, que se ha actualizado recientemente. La versión actual ahora es v1.8.He aquí un ejemplo para crear una postal:

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

He aquí un ejemplo de actualización una postal existente; no es necesario para extraer los archivos de hacerlo:

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

He aquí un ejemplo que extrae entradas:

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

DotNetZip es compatible con caracteres de varios bytes en los nombres de archivos, cifrado Zip, el cifrado AES, arroyos , Unicode, archivos autoextraíbles. También hace ZIP64, para longitudes de archivo superiores a 0xFFFFFFFF, o para archivos con más de 65535 entradas.

gratis. de código abierto

lo consigue en codeplex

+1

cheeso, estoy de acuerdo contigo, pero no soy capaz de construir el código que he descargado de codeplex. Por favor, di cómo construir ... si construyo la solución principal, su lote de lanzamiento o errores ... no sé cómo construir – Naruto

+4

¿por qué lo estás construyendo? Hay un binario Descargue la DLL. – Cheeso

+1

¿Por qué recomendar una biblioteca de terceros, no debería bastar el espacio de nombres 'System.IO.Packaging'? ¿O es el último párrafo que detalla qué incluye la funcionalidad ** .NET framework Zip incorporada? –

44

De MSDN,

En este ejemplo, se utiliza la clase de paquete (en contraposición a la ZipPackage.) Después de haber trabajado con ambos, sólo he visto suceder descamación cuando hay corrupción en el archivo zip. No necesariamente la corrupción que arroja el extractor de Windows o Winzip, pero algo que los componentes de embalaje tienen problemas para manejar.

Espero que esto ayude, tal vez puede proporcionarle una alternativa a la depuración del problema.

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

Al mirar ese código, simplemente vomité en mis zapatos. PackagePartCollection? PartRelationship? PackagePart? ¿URI de parte? ToLowerInvariant? Todo lo que quería era un archivo ZIP ... – Cheeso

+2

Sí, esa sería la parte que los desarrolladores de OpenPackage parecían olvidar. Trabajar con OpenPackage es mucho más acerca de trabajar con los componentes virtuales, a diferencia de la representación física. – jro

+15

Esta es la única respuesta que responde a la pregunta real de cómo uso X para hacer Y, tiene código y todo, no va en una tangente y muestra cómo usar Z para hacer Y, y es que tiene la menor ¿votos? Vamos gente. – a7drew

29

De "ZipPackage Class" (MSDN):

mientras que los paquetes se almacenan como archivos Zip * a través de la clase ZipPackage, todos los archivos Zip no son ZipPackages. Un ZipPackage tiene requisitos especiales como nombres de archivos (parte) compatibles con URI y un archivo "[Content_Types] .xml" que define los tipos MIME para todos los archivos contenidos en el paquete. La clase ZipPackage no se puede usar para abrir archivos Zip arbitrarios que no cumplan con el estándar de Convenciones de empaquetado abierto.

Para más detalles, véase la Sección 9.2 "Asignación a un archivo ZIP" del estándar ECMA Internacional "Open Convenciones de empaquetado", http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%20Part%202%20(DOCX).zip (342Kb) o http://www.ecma-international.org/publications/files/ECMA-ST/Office%20Open%20XML%20Part%202%20(PDF).zip (1.3Mb)

* Usted puede simplemente añadir" .zip "a la extensión de cualquier archivo basado en ZipPackage (.docx, .xlsx, .pptx, etc.) para abrirlo en su utilidad Zip favorita.

+0

¡Eso es útil, gracias! –

6

Estoy de acuerdo withe Cheeso. System.IO.Packaging es incómodo cuando se manejan archivos zip genéricos, ya que fue diseñado para documentos Office Open XML. Sugeriría usar DotNetZip o SharpZipLib

12

¡Estaba teniendo exactamente el mismo problema! Para que el método GetParts() devuelva algo, tuve que agregar el archivo [Content_Types] .xml a la raíz del archivo con un nodo "Predeterminado" para cada extensión de archivo incluida. Una vez que agregué esto (solo usando Windows Explorer), mi código pudo leer y extraer los contenidos archivados.

Más información sobre el archivo .xml [Content_Types] se puede encontrar aquí:

http://msdn.microsoft.com/en-us/magazine/cc163372.aspx - No es un archivo de ejemplo a continuación la figura 13 del artículo.

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

Nota: Este código utiliza el método Stream.CopyTo en .NET 4.0

+1

¡Gracias por responder la pregunta de la manera en que se hizo! – shytikov

1

(Esto es básicamente una reformulación de this answer)

Resulta que System.IO.Packaging.ZipPackage no es compatible con PKZIP, por eso cuando abre un archivo ZIP "genérico", no se devuelven "partes". Esta clase solo admite algunos sabores específicos de archivos ZIP (consulte los comentarios en la parte inferior de MSDN description) utilizados, entre otros, como paquetes de servicio de Windows Azure hasta SDK 1.6. Por eso, si descomprime un paquete de servicio y lo vuelve a embalar usando el paquete de información. se convertirá en inválido.

Cuestiones relacionadas