Creating a ZIP archive in memory using System.IO.Compression

I'm trying to create a ZIP archive with a simple demo text file using a MemoryStream as follows:

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

If I run this code, the archive file itself is created but foo.txt isn't.

However, if I replace the MemoryStream directly with the file stream, the archive is created correctly:

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

Is it possible to use a MemoryStream to create the ZIP archive without the FileStream?


Solution 1:

Thanks to ZipArchive creates invalid ZIP file, I got:

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

That indicated we need to call Dispose on ZipArchive before we can use it, which as Amir suggests is likely because it writes final bytes like checksum to the archive that makes it complete. But in order not close the stream so we can re-use it after you need to pass true as the third parameter to ZipArchive.

Solution 2:

Just another version of zipping without writing any file.

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

Solution 3:

Set the position of the stream to the 0 before copying it to the zip stream.

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

Solution 4:

Working solution for 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);
    }

Solution 5:

You need to finish writing the memory stream then read the buffer back.

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