Crear un archivo ZIP en la memoria usando System.IO.Compression

179

Estoy tratando de crear un archivo ZIP con un archivo de texto de demostración simple usando el MemoryStreamsiguiente:

using (var memoryStream = new MemoryStream())
using (var archive = new ZipArchive(memoryStream , ZipArchiveMode.Create))
{
    var demoFile = archive.CreateEntry("foo.txt");

    using (var entryStream = demoFile.Open())
    using (var streamWriter = new StreamWriter(entryStream))
    {
        streamWriter.Write("Bar!");
    }

    using (var fileStream = new FileStream(@"C:\Temp\test.zip", FileMode.Create))
    {
        stream.CopyTo(fileStream);
    }
}

Si ejecuto este código, se crea el archivo de almacenamiento en sí, pero foo.txt no.

Sin embargo, si reemplazo el MemoryStreamarchivo directamente con la secuencia del archivo, el archivo se crea correctamente:

using (var fileStream = new FileStream(@"C:\Temp\test.zip", FileMode.Create))
using (var archive = new ZipArchive(fileStream, FileMode.Create))
{
    // ...
}

¿Es posible usar un MemoryStreampara crear el archivo ZIP sin el FileStream?

Marius Schulz
fuente
¿podría tener algo que ver con disponer? por ejemplo , no lo hacen usingpor entry.Open, solo una suposición.
Sinatr
@Sinatr: En ese caso, debería haber obtenido una ObjectDisposedException o al menos un archivo vacío, ¿no?
Marius Schulz
La posición de sus archivos va al final del archivo, por lo que no puede escribir nada, por lo que si intenta restablecer la propiedad de posición de la secuencia a 0, funcionará.
Mahesh
Obsoleto .. (¡No sabía sobre la opción "no desechar la corriente de base" mencionada en la respuesta!)
El Dag
FYI: se necesita al menos .NET 4.5 para .ZipArchive. Vea .NET 4.5 de Rick Strahl es un reemplazo en el lugar para .NET 4.0 ~~ vea también no encontré la clase "ZipFile" en el espacio de nombres "System.IO.Compression"
gerryLowry

Respuestas:

300

Gracias a https://stackoverflow.com/a/12350106/222748 obtuve:

using (var memoryStream = new MemoryStream())
{
   using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
   {
      var demoFile = archive.CreateEntry("foo.txt");

      using (var entryStream = demoFile.Open())
      using (var streamWriter = new StreamWriter(entryStream))
      {
         streamWriter.Write("Bar!");
      }
   }

   using (var fileStream = new FileStream(@"C:\Temp\test.zip", FileMode.Create))
   {
      memoryStream.Seek(0, SeekOrigin.Begin);
      memoryStream.CopyTo(fileStream);
   }
}

Por lo tanto, debemos llamar a dispose en ZipArchive antes de poder usarlo, lo que significa pasar 'true' como el tercer parámetro a ZipArchive para que podamos acceder a la secuencia después de desecharlo.

Miguel
fuente
24
Parece que ZipArchive agrega una suma de verificación y finaliza el archivo cuando lo desecha, y si usa la secuencia antes de deshacerse, el archivo se corrompe.
Amir
@Amir, ¿puedes explicar esto?
Josh Stodola
@JoshStodola ZipArchive agrega una suma de verificación al final del archivo para determinar la corrupción o el cambio (razones de seguridad). Entonces, hasta que cierre el archivo o lo elimine, la suma de verificación no se incluye en la secuencia. Espero explicarlo claro!
Amir
¿Se puede usar para leer archivos de un ziparchive que se transmitió? Por ejemplo, un archivo transmitido desde la web?
Kraang Prime
59

Solo otra versión de compresión sin escribir ningún archivo.

string fileName = "export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".xlsx";
byte[] fileBytes = here is your file in bytes
byte[] compressedBytes;
string fileNameZip = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";

using (var outStream = new MemoryStream())
{
    using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
    {
        var fileInArchive = archive.CreateEntry(fileName, CompressionLevel.Optimal);
        using (var entryStream = fileInArchive.Open())
        using (var fileToCompressStream = new MemoryStream(fileBytes))
        {
            fileToCompressStream.CopyTo(entryStream);
        }
    }
    compressedBytes = outStream.ToArray();
}
luci79rom
fuente
¡Gracias! Acabo de agregar la línea de código para convertir bytes a archivo zip
Elnoor
14

Establezca la posición de la secuencia en 0 antes de copiarla en la secuencia zip.

using (var memoryStream = new MemoryStream())
{
 using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
 {
  var demoFile = archive.CreateEntry("foo.txt");

  using (var entryStream = demoFile.Open())
  using (var streamWriter = new StreamWriter(entryStream))
  {
     streamWriter.Write("Bar!");
  }
 }

 using (var fileStream = new FileStream(@"C:\Temp\test.zip", FileMode.Create))
   {
     memoryStream.Position=0;
     memoryStream.WriteTo(fileStream);
   }
 }
Mahesh
fuente
9

Solución de trabajo para MVC

    public ActionResult Index()
    {
        string fileName = "test.pdf";
        string fileName1 = "test.vsix";
        string fileNameZip = "Export_" + DateTime.Now.ToString("yyyyMMddhhmmss") + ".zip";

        byte[] fileBytes = System.IO.File.ReadAllBytes(@"C:\test\test.pdf");
        byte[] fileBytes1 = System.IO.File.ReadAllBytes(@"C:\test\test.vsix");
        byte[] compressedBytes;
        using (var outStream = new MemoryStream())
        {
            using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
            {
                var fileInArchive = archive.CreateEntry(fileName, CompressionLevel.Optimal);
                using (var entryStream = fileInArchive.Open())
                using (var fileToCompressStream = new MemoryStream(fileBytes))
                {
                    fileToCompressStream.CopyTo(entryStream);
                }

                var fileInArchive1 = archive.CreateEntry(fileName1, CompressionLevel.Optimal);
                using (var entryStream = fileInArchive1.Open())
                using (var fileToCompressStream = new MemoryStream(fileBytes1))
                {
                    fileToCompressStream.CopyTo(entryStream);
                }


            }
            compressedBytes = outStream.ToArray();
        }
        return File(compressedBytes, "application/zip", fileNameZip);
    }
Debajit Mukhopadhyay
fuente
1
El Controller.Filemétodo tiene una sobrecarga que aceptaStream . Úselo para evitar crear otra copia de un archivo ZIP en la memoria.
Martin Prikryl
6

Debe terminar de escribir el flujo de memoria y luego volver a leer el búfer.

        using (var memoryStream = new MemoryStream())
        {
            using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create))
            {
                var demoFile = archive.CreateEntry("foo.txt");

                using (var entryStream = demoFile.Open())
                using (var streamWriter = new StreamWriter(entryStream))
                {
                    streamWriter.Write("Bar!");
                }
            }

            using (var fileStream = new FileStream(@"C:\Temp\test.zip", FileMode.Create))
            {
                var bytes = memoryStream.GetBuffer();
                fileStream.Write(bytes,0,bytes.Length );
            }
        }
Traqueteo
fuente
2
using System;
using System.IO;
using System.IO.Compression;

namespace ConsoleApplication
{
    class Program`enter code here`
    {
        static void Main(string[] args)
        {
            using (FileStream zipToOpen = new FileStream(@"c:\users\exampleuser\release.zip", FileMode.Open))
            {
                using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
                {
                    ZipArchiveEntry readmeEntry = archive.CreateEntry("Readme.txt");
                    using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
                    {
                            writer.WriteLine("Information about this package.");
                            writer.WriteLine("========================");
                    }
                }
            }
        }
    }
}
FAREH
fuente
1

Esta es la forma de convertir una entidad en un archivo XML y luego comprimirla:

private  void downloadFile(EntityXML xml) {

string nameDownloadXml = "File_1.xml";
string nameDownloadZip = "File_1.zip";

var serializer = new XmlSerializer(typeof(EntityXML));

Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.AddHeader("content-disposition", "attachment;filename=" + nameDownloadZip);

using (var memoryStream = new MemoryStream())
{
    using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
    {
        var demoFile = archive.CreateEntry(nameDownloadXml);
        using (var entryStream = demoFile.Open())
        using (StreamWriter writer = new StreamWriter(entryStream, System.Text.Encoding.UTF8))
        {
            serializer.Serialize(writer, xml);
        }
    }

    using (var fileStream = Response.OutputStream)
    {
        memoryStream.Seek(0, SeekOrigin.Begin);
        memoryStream.CopyTo(fileStream);
    }
}

Response.End();

}

luisfbn
fuente
-1

Función para devolver la secuencia que contiene el archivo zip

public static Stream ZipGenerator(List<string> files)
    {
        ZipArchiveEntry fileInArchive;
        Stream entryStream;
        int i = 0;
        List<byte[]> byteArray = new List<byte[]>();

        foreach (var file in files)
        {
            byteArray.Add(File.ReadAllBytes(file));
        }

        var outStream = new MemoryStream();

        using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true))
        {
            foreach (var file in files)
            {
                fileInArchive=(archive.CreateEntry(Path.GetFileName(file), CompressionLevel.Optimal));

                using (entryStream = fileInArchive.Open())
                {
                        using (var fileToCompressStream = new MemoryStream(byteArray[i]))
                        {
                            fileToCompressStream.CopyTo(entryStream);
                        }
                        i++;
                }
            }
        }
        outStream.Position = 0;
        return outStream;
    }

Si lo desea, escriba zip en la secuencia de archivos.

using (var fileStream = new FileStream(@"D:\Tools\DBExtractor\DBExtractor\bin\Debug\test.zip", FileMode.Create))
{
   outStream.Position = 0;
   outStream.WriteTo(fileStream);
}

``

Arun CS
fuente
Leer todos los archivos en la memoria primero es una gran pérdida de memoria. Terminas con todos los archivos dos veces en la memoria. Una vez en el byteArrayy una vez en el ZipArchive. Sin mencionar que no necesita cargar archivos en la memoria en absoluto. Utilice la transmisión, como se muestra en todas las otras respuestas existentes.
Martin Prikryl
@MartinPrikryl La pregunta es una forma de escribir un archivo zip en la memoria. Por eso uso la memoria. por supuesto, la mejor manera es escribir en local
Arun CS
Sé de qué se trata la pregunta. El punto de mi comentario es que su implementación de crear un archivo ZIP en la memoria es muy ineficiente. Las implementaciones en otras respuestas son mejores.
Martin Prikryl
-5
       private void button6_Click(object sender, EventArgs e)
    {

        //create With Input FileNames
        AddFileToArchive_InputByte(new ZipItem[]{ new ZipItem( @"E:\b\1.jpg",@"images\1.jpg"),
            new ZipItem(@"E:\b\2.txt",@"text\2.txt")}, @"C:\test.zip");

        //create with input stream
        AddFileToArchive_InputByte(new ZipItem[]{ new ZipItem(File.ReadAllBytes( @"E:\b\1.jpg"),@"images\1.jpg"),
            new ZipItem(File.ReadAllBytes(@"E:\b\2.txt"),@"text\2.txt")}, @"C:\test.zip");

        //Create Archive And Return StreamZipFile
        MemoryStream GetStreamZipFile = AddFileToArchive(new ZipItem[]{ new ZipItem( @"E:\b\1.jpg",@"images\1.jpg"),
            new ZipItem(@"E:\b\2.txt",@"text\2.txt")});


        //Extract in memory
        ZipItem[] ListitemsWithBytes = ExtractItems(@"C:\test.zip");

        //Choese Files For Extract To memory
        List<string> ListFileNameForExtract = new List<string>(new string[] { @"images\1.jpg", @"text\2.txt" });
        ListitemsWithBytes = ExtractItems(@"C:\test.zip", ListFileNameForExtract);


        // Choese Files For Extract To Directory
        ExtractItems(@"C:\test.zip", ListFileNameForExtract, "c:\\extractFiles");

    }

    public struct ZipItem
    {
        string _FileNameSource;
        string _PathinArchive;
        byte[] _Bytes;
        public ZipItem(string __FileNameSource, string __PathinArchive)
        {
            _Bytes=null ;
            _FileNameSource = __FileNameSource;
            _PathinArchive = __PathinArchive;
        }
        public ZipItem(byte[] __Bytes, string __PathinArchive)
        {
            _Bytes = __Bytes;
            _FileNameSource = "";
            _PathinArchive = __PathinArchive;

        }
        public string FileNameSource
        {
            set
            {
                FileNameSource = value;
            }
            get
            {
                return _FileNameSource;
            }
        }
        public string PathinArchive
        {
            set
            {
                _PathinArchive = value;
            }
            get
            {
                return _PathinArchive;
            }
        }
        public byte[] Bytes
        {
            set
            {
                _Bytes = value;
            }
            get
            {
                return _Bytes;
            }
        }
    }


     public void AddFileToArchive(ZipItem[] ZipItems, string SeveToFile)
    {

        MemoryStream memoryStream = new MemoryStream();

        //Create Empty Archive
        ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);

        foreach (ZipItem item in ZipItems)
        {

            //Create Path File in Archive
            ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);


            //Open File in Archive For Write
            var OpenFileInArchive = FileInArchive.Open();

            //Read Stream
            FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);

            byte[] ReadAllbytes = new byte[4096];//Capcity buffer
            int ReadByte = 0;
            while (fsReader.Position != fsReader.Length)
            {
                //Read Bytes
                ReadByte = fsReader.Read(ReadAllbytes, 0, ReadAllbytes.Length);

                //Write Bytes
                OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
            }
            fsReader.Dispose();
            OpenFileInArchive.Close();


        }
        archive.Dispose();

        using (var fileStream = new FileStream(SeveToFile, FileMode.Create))
        {
            memoryStream.Seek(0, SeekOrigin.Begin);
            memoryStream.CopyTo(fileStream);
        }





    }
     public MemoryStream  AddFileToArchive(ZipItem[] ZipItems)
    {

        MemoryStream memoryStream = new MemoryStream();

        //Create Empty Archive
        ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);

        foreach (ZipItem item in ZipItems)
        {

            //Create Path File in Archive
            ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);


            //Open File in Archive For Write
            var OpenFileInArchive = FileInArchive.Open();

            //Read Stream
            FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);

            byte[] ReadAllbytes = new byte[4096];//Capcity buffer
            int ReadByte = 0;
            while (fsReader.Position != fsReader.Length)
            {
                //Read Bytes
                ReadByte = fsReader.Read(ReadAllbytes, 0, ReadAllbytes.Length);

                //Write Bytes
                OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
            }
            fsReader.Dispose();
            OpenFileInArchive.Close();


        }
        archive.Dispose();




        return memoryStream;


    }

     public void AddFileToArchive_InputByte(ZipItem[] ZipItems, string SeveToFile)
    {

        MemoryStream memoryStream = new MemoryStream();

        //Create Empty Archive
        ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);

        foreach (ZipItem item in ZipItems)
        {

            //Create Path File in Archive
            ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);


            //Open File in Archive For Write
            var OpenFileInArchive = FileInArchive.Open();

            //Read Stream
          //  FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);

            byte[] ReadAllbytes = new byte[4096];//Capcity buffer
            int ReadByte = 4096 ;int  TotalWrite=0;
            while (TotalWrite != item.Bytes.Length)
            {

                if(TotalWrite+4096>item.Bytes.Length)
                 ReadByte=item.Bytes.Length-TotalWrite;



                Array.Copy(item.Bytes, TotalWrite, ReadAllbytes, 0, ReadByte);




                //Write Bytes
                OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
                TotalWrite += ReadByte;
            }

            OpenFileInArchive.Close();


        }
        archive.Dispose();

        using (var fileStream = new FileStream(SeveToFile, FileMode.Create))
        {
            memoryStream.Seek(0, SeekOrigin.Begin);
            memoryStream.CopyTo(fileStream);
        }


    }
     public MemoryStream  AddFileToArchive_InputByte(ZipItem[] ZipItems)
    {

        MemoryStream memoryStream = new MemoryStream();

        //Create Empty Archive
        ZipArchive archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true);

        foreach (ZipItem item in ZipItems)
        {

            //Create Path File in Archive
            ZipArchiveEntry FileInArchive = archive.CreateEntry(item.PathinArchive);


            //Open File in Archive For Write
            var OpenFileInArchive = FileInArchive.Open();

            //Read Stream
          //  FileStream fsReader = new FileStream(item.FileNameSource, FileMode.Open, FileAccess.Read);

            byte[] ReadAllbytes = new byte[4096];//Capcity buffer
            int ReadByte = 4096 ;int  TotalWrite=0;
            while (TotalWrite != item.Bytes.Length)
            {

                if(TotalWrite+4096>item.Bytes.Length)
                 ReadByte=item.Bytes.Length-TotalWrite;



                Array.Copy(item.Bytes, TotalWrite, ReadAllbytes, 0, ReadByte);




                //Write Bytes
                OpenFileInArchive.Write(ReadAllbytes, 0, ReadByte);
                TotalWrite += ReadByte;
            }

            OpenFileInArchive.Close();


        }
        archive.Dispose();



        return memoryStream;
    }

     public void ExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName)
     {
         //Opens the zip file up to be read
         using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
         {
             if (Directory.Exists(destinationDirectoryName)==false )
                 Directory.CreateDirectory(destinationDirectoryName);

             //Loops through each file in the zip file
             archive.ExtractToDirectory(destinationDirectoryName);

         }
     }  
     public void   ExtractItems(string sourceArchiveFileName,List< string> _PathFilesinArchive, string destinationDirectoryName)
     {

         //Opens the zip file up to be read
         using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
         {


             //Loops through each file in the zip file
             foreach (ZipArchiveEntry file in archive.Entries)
             {
                 int PosResult = _PathFilesinArchive.IndexOf(file.FullName);
                 if (PosResult != -1)
                 {
                     //Create Folder
                     if (Directory.Exists( destinationDirectoryName + "\\" +Path.GetDirectoryName( _PathFilesinArchive[PosResult])) == false)
                         Directory.CreateDirectory(destinationDirectoryName + "\\" + Path.GetDirectoryName(_PathFilesinArchive[PosResult]));

                     Stream OpenFileGetBytes = file.Open();

                     FileStream   FileStreamOutput = new FileStream(destinationDirectoryName + "\\" + _PathFilesinArchive[PosResult], FileMode.Create);

                     byte[] ReadAllbytes = new byte[4096];//Capcity buffer
                     int ReadByte = 0; int TotalRead = 0; 
                     while (TotalRead != file.Length)
                     {
                         //Read Bytes
                         ReadByte = OpenFileGetBytes.Read(ReadAllbytes, 0, ReadAllbytes.Length);
                         TotalRead += ReadByte;

                         //Write Bytes
                         FileStreamOutput.Write(ReadAllbytes, 0, ReadByte);
                     }

                     FileStreamOutput.Close();
                     OpenFileGetBytes.Close();



                     _PathFilesinArchive.RemoveAt(PosResult);
                 }

                 if (_PathFilesinArchive.Count == 0)
                     break;
             }
         }


     }

     public ZipItem[] ExtractItems(string sourceArchiveFileName)
     {
       List<  ZipItem> ZipItemsReading = new List<ZipItem>();
         //Opens the zip file up to be read
         using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
         {


             //Loops through each file in the zip file
             foreach (ZipArchiveEntry file in archive.Entries)
             {
                 Stream OpenFileGetBytes = file.Open();

                 MemoryStream memstreams = new MemoryStream();
                 byte[] ReadAllbytes = new byte[4096];//Capcity buffer
                 int ReadByte = 0; int TotalRead = 0;
                 while (TotalRead != file.Length)
                 {
                     //Read Bytes
                     ReadByte = OpenFileGetBytes.Read(ReadAllbytes, 0, ReadAllbytes.Length);
                     TotalRead += ReadByte;

                     //Write Bytes
                     memstreams.Write(ReadAllbytes, 0, ReadByte);
                 }

                 memstreams.Position = 0;
                 OpenFileGetBytes.Close();
                 memstreams.Dispose();

                 ZipItemsReading.Add(new ZipItem(memstreams.ToArray(),file.FullName));


             }
         }

         return ZipItemsReading.ToArray();
     }
     public ZipItem[] ExtractItems(string sourceArchiveFileName,List< string> _PathFilesinArchive)
     {
       List<  ZipItem> ZipItemsReading = new List<ZipItem>();
         //Opens the zip file up to be read
         using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
         {

             //Loops through each file in the zip file
             foreach (ZipArchiveEntry file in archive.Entries)
             {
                 int PosResult = _PathFilesinArchive.IndexOf(file.FullName); 
                 if (PosResult!= -1)
                 {
                     Stream OpenFileGetBytes = file.Open();

                     MemoryStream memstreams = new MemoryStream();
                     byte[] ReadAllbytes = new byte[4096];//Capcity buffer
                     int ReadByte = 0; int TotalRead = 0;  
                     while (TotalRead != file.Length)
                     {
                         //Read Bytes
                         ReadByte = OpenFileGetBytes.Read(ReadAllbytes, 0, ReadAllbytes.Length);
                         TotalRead += ReadByte;

                         //Write Bytes
                         memstreams.Write(ReadAllbytes, 0, ReadByte);
                     }

                     //Create item
                     ZipItemsReading.Add(new ZipItem(memstreams.ToArray(),file.FullName));

                     OpenFileGetBytes.Close();
                     memstreams.Dispose();



                     _PathFilesinArchive.RemoveAt(PosResult);
                 }

                 if (_PathFilesinArchive.Count == 0)
                     break;


             }
         }

         return ZipItemsReading.ToArray();
     }
aliastitan
fuente