2008-11-09 76 views
25

¿Hay alguna forma de crear un archivo Zip que contenga varios archivos cuando los archivos están actualmente en la memoria? Los archivos que quiero guardar son en realidad solo texto y están almacenados en una clase de cadena en mi aplicación. Pero me gustaría guardar varios archivos en un solo archivo autónomo. Todos pueden estar en la raíz del archivo.Crear archivo Zip desde múltiples archivos de memoria en C#

Sería bueno poder hacer esto con SharpZipLib.

Respuesta

25

Use ZipEntry y PutNextEntry() para esto. A continuación se muestra cómo hacerlo para un archivo, pero para un objeto en memoria sólo tiene que utilizar un MemoryStream

FileStream fZip = File.Create(compressedOutputFile); 
ZipOutputStream zipOStream = new ZipOutputStream(fZip); 
foreach (FileInfo fi in allfiles) 
{ 
    ZipEntry entry = new ZipEntry((fi.Name)); 
    zipOStream.PutNextEntry(entry); 
    FileStream fs = File.OpenRead(fi.FullName); 
    try 
    { 
     byte[] transferBuffer[1024]; 
     do 
     { 
      bytesRead = fs.Read(transferBuffer, 0, transferBuffer.Length); 
      zipOStream.Write(transferBuffer, 0, bytesRead); 
     } 
     while (bytesRead > 0); 
    } 
    finally 
    { 
     fs.Close(); 
    } 
} 
zipOStream.Finish(); 
zipOStream.Close(); 
+0

¿Se puede usar 'using()' en 'zipOStream' en lugar de' Close() '? –

+0

Sí, ZipOutputStream es desechable – Mike

+2

Si está utilizando .NET 4.5, hay una clase Zip incorporada que es mucho más fácil de usar que esta abominación (SharpZipLib). –

5

Sí, puede usar SharpZipLib para hacer esto: cuando necesite suministrar una secuencia para escribir, use un MemoryStream.

-1

Use un StringReader para leer de sus objetos de cadena y exponerlos como Stream s.

Eso debería hacer que sea más fácil alimentarlos a su código de creación de zip.

2

Nota es obsoleta esta respuesta; desde .Net 4.5, la clase ZipArchive permite comprimir archivos en la memoria. Consulte johnny 5's answer below para saber cómo usarlo.


También podría hacerlo un poco diferente, utilizando un objeto Serializable para almacenar todas las cadenas

[Serializable] 
public class MyStrings { 
    public string Foo { get; set; } 
    public string Bar { get; set; } 
} 

Entonces, usted podría serializar en una corriente para guardarlo.
Para ahorrar espacio, puede usar GZipStream (desde System.IO.Compression) para comprimirlo. (nota: GZip es compresión de flujo, no un archivo de múltiples archivos).

Esto es, por supuesto, si lo que necesita es realmente guardar datos, y no comprimir unos pocos archivos en un formato específico para otro software. Además, esto le permitiría guardar muchos más tipos de datos excepto cadenas.

+0

Quería hacer que el archivo guardado tuviera un formato que permitiera a un usuario abrirlo y sacar piezas específicas, pero eso puede ser lo mejor/más fácil de hacer ... –

+0

@Configurator ¿Te apetece verte aquí? –

19

Usar SharpZipLib para esto parece bastante complicado. Esto es mucho más fácil en DotNetZip. En v1.9, el código es el siguiente:

using (ZipFile zip = new ZipFile()) 
{ 
    zip.AddEntry("Readme.txt", stringContent1); 
    zip.AddEntry("readings/Data.csv", stringContent2); 
    zip.AddEntry("readings/Index.xml", stringContent3); 
    zip.Save("Archive1.zip"); 
} 

El código anterior supone stringContent {1,2,3} contiene los datos que se almacenan en los archivos (o entradas) en el archivo zip. La primera entrada es "Readme.txt" y se almacena en el "directorio" de nivel superior en el archivo zip. Las siguientes dos entradas se almacenan en el directorio "lecturas" en el archivo zip.

Las cadenas están codificadas en la codificación predeterminada. Hay una sobrecarga de AddEntry(), que no se muestra aquí, que le permite especificar explícitamente la codificación que se usará.

Si tiene el contenido en una secuencia o matriz de bytes, no una cadena, hay sobrecargas para AddEntry() que aceptan esos tipos. También hay sobrecargas que aceptan un delegado de escritura, un método tuyo que se invoca para escribir datos en el zip. Esto funciona para guardar fácilmente un DataSet en un archivo zip, por ejemplo.

DotNetZip es gratuito y de código abierto.

4

me encuentro con este problema, utilizando el ejemplo de MSDN creé esta clase:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO.Packaging; 
using System.IO; 

public class ZipSticle 
{ 
    Package package; 

    public ZipSticle(Stream s) 
    { 
     package = ZipPackage.Open(s, FileMode.Create); 
    } 

    public void Add(Stream stream, string Name) 
    { 
     Uri partUriDocument = PackUriHelper.CreatePartUri(new Uri(Name, UriKind.Relative)); 
     PackagePart packagePartDocument = package.CreatePart(partUriDocument, ""); 

     CopyStream(stream, packagePartDocument.GetStream()); 
     stream.Close(); 
    } 

    private static void CopyStream(Stream source, Stream target) 
    { 
     const int bufSize = 0x1000; 
     byte[] buf = new byte[bufSize]; 
     int bytesRead = 0; 
     while ((bytesRead = source.Read(buf, 0, bufSize)) > 0) 
      target.Write(buf, 0, bytesRead); 
    } 

    public void Close() 
    { 
     package.Close(); 
    } 
} 

entonces usted puede utilizar de esta manera:

FileStream str = File.Open("MyAwesomeZip.zip", FileMode.Create); 
ZipSticle zip = new ZipSticle(str); 

zip.Add(File.OpenRead("C:/Users/C0BRA/SimpleFile.txt"), "Some directory/SimpleFile.txt"); 
zip.Add(File.OpenRead("C:/Users/C0BRA/Hurp.derp"), "hurp.Derp"); 

zip.Close(); 
str.Close(); 

puede pasar un MemoryStream (o cualquier Stream) a ZipSticle.Add como:

FileStream str = File.Open("MyAwesomeZip.zip", FileMode.Create); 
ZipSticle zip = new ZipSticle(str); 

byte[] fileinmem = new byte[1000]; 
// Do stuff to FileInMemory 
MemoryStream memstr = new MemoryStream(fileinmem); 
zip.Add(memstr, "Some directory/SimpleFile.txt"); 

memstr.Close(); 
zip.Close(); 
str.Close(); 
2

Esta función debe crear una matriz de bytes de un flujo de datos: He creado una interfaz sencilla para el manejo de archivos de simplicidad

public interface IHasDocumentProperties 
{ 
    byte[] Content { get; set; } 
    string Name { get; set; } 
} 

public void CreateZipFileContent(string filePath, IEnumerable<IHasDocumentProperties> fileInfos) 
{  
    using (var memoryStream = new MemoryStream()) 
    using (var zipArchive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) 
    { 
     foreach(var fileInfo in fileInfos) 
     { 
      var entry = zipArchive.CreateEntry(fileInfo.Name); 

      using (var entryStream = entry.Open()) 
      { 
       entryStream.Write(fileInfo.Content, 0, fileInfo.Content.Length); 
      }       
     } 
    } 

    using (var fileStream = new FileStream(filePath, FileMode.OpenOrCreate, System.IO.FileAccess.Write)) 
    { 
     memoryStream.CopyTo(fileStream); 
    } 
} 
0

que estaba utilizando Cheeso's answer añadiendo MemoryStream s como la fuente de los diferentes archivos de Excel. Cuando descargué el archivo comprimido, los archivos no tenían nada en ellos. Esta podría ser la forma en que estábamos tratando de crear y descargar un archivo a través de AJAX.

Para obtener los contenidos de los diferentes archivos de Excel que se incluirán en el Zip, tuve que agregar cada uno de los archivos como byte[].

using (var memoryStream = new MemoryStream()) 
using (var zip = new ZipFile()) 
{ 
    zip.AddEntry("Excel File 1.xlsx", excelFileStream1.ToArray()); 
    zip.AddEntry("Excel File 2.xlsx", excelFileStream2.ToArray()); 

    // Keep the file off of disk, and in memory. 
    zip.Save(memoryStream); 
} 
Cuestiones relacionadas