72

I am trying to create a Zip file in .NET 4.5 (System.IO.Compression) from a series of byte arrays. As an example, from an API I am using I end up with a List<Attachment> and each Attachment has a property called Body which is a byte[]. How can I iterate over that list and create a zip file that contains each attachment?

Right now I am under the impression that I would have to write each attachment to disk and create the zip file from that.

//This is great if I had the files on disk
ZipFile.CreateFromDirectory(startPath, zipPath);
//How can I create it from a series of byte arrays?
3

3 Answers 3

140

After a little more playing around and reading I was able to figure this out. Here is how you can create a zip file (archive) with multiple files without writing any temporary data to disk:

using (var compressedFileStream = new MemoryStream())
{
    //Create an archive and store the stream in memory.
    using (var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Create, false)) {
        foreach (var caseAttachmentModel in caseAttachmentModels) {
            //Create a zip entry for each attachment
            var zipEntry = zipArchive.CreateEntry(caseAttachmentModel.Name);

            //Get the stream of the attachment
            using (var originalFileStream = new MemoryStream(caseAttachmentModel.Body))
            using (var zipEntryStream = zipEntry.Open()) {
                //Copy the attachment stream to the zip entry stream
                originalFileStream.CopyTo(zipEntryStream);
            }
        }
    }

    return new FileContentResult(compressedFileStream.ToArray(), "application/zip") { FileDownloadName = "Filename.zip" };
}
18
  • 15
    Friendly tip - If the ZipArchive class is unavailable, add a reference to the assembly: Right-Click References -> Add Reference. Under Assemblies, search for System.IO and check off System.IO.Compression.FileSystem and System.IO.Compression. Nov 29, 2016 at 2:49
  • 4
    What is caseAttachmentModels? Also where is the byte[]?
    – Demodave
    Dec 22, 2016 at 22:29
  • 3
    I used this but the "compressedFileStream" is always at 0 length... any ideas? Jul 25, 2018 at 9:54
  • 5
    @krillgar your edit introduced a bug. The original has the return statement after the zipArchive is closed. By removing the nesting, you also now have the return statement inside the zipArchive using statement. By not disposing, the archives are not valid.
    – Zeph
    May 11, 2020 at 19:25
  • 4
    @Zeph is correct. I had to add the braces around the original using statement and move the return statement one level out to make my zipArchive valid.
    – George
    Jun 11, 2020 at 4:50
8

This is a variation of the great accepted answer posted by the OP. However, this is for WebForms instead of MVC. I'm working with the assumption that caseAttachmentModel.Body is a byte[]

Essentially everything is the same except with an additional method that sends the zip out as a Response.

using (var compressedFileStream = new MemoryStream()) {
    //Create an archive and store the stream in memory.
    using (var zipArchive = new ZipArchive(compressedFileStream, ZipArchiveMode.Update, false))         {
     foreach (var caseAttachmentModel in caseAttachmentModels) {
        //Create a zip entry for each attachment
        var zipEntry = zipArchive.CreateEntry(caseAttachmentModel.Name);

        //Get the stream of the attachment
        using (var originalFileStream = new MemoryStream(caseAttachmentModel.Body)) {
                using (var zipEntryStream = zipEntry.Open()) {
                    //Copy the attachment stream to the zip entry stream
                    originalFileStream.CopyTo(zipEntryStream);
                }
            }
        }
    }
    sendOutZIP(compressedFileStream.ToArray(), "FileName.zip");
}

private void sendOutZIP(byte[] zippedFiles, string filename)
{
    Response.Clear();
    Response.ClearContent();
    Response.ClearHeaders();
    Response.ContentType = "application/x-compressed";
    Response.Charset = string.Empty;
    Response.Cache.SetCacheability(System.Web.HttpCacheability.Public);
    Response.AddHeader("Content-Disposition", "attachment; filename=" + filename);
    Response.BinaryWrite(zippedFiles);
    Response.OutputStream.Flush();
    Response.OutputStream.Close();
    Response.End();
}

I would also like to point out that advice given by @Levi Fuller on references in the accepted answer is spot on!

2
2

GZipStream and DeflateStream seem like they would let you use steams/byte arrays to solve your problem, but maybe not with a compression file format usable by most users. (ie, your file would have a .gz extension) If this file is only used internally, that might be okay.

I don't know how you might make a ZIP using Microsoft's libraries, but I remember this library supporting the sort of things you might find useful: http://sevenzipsharp.codeplex.com/

It's licensed under LGPL.

2
  • I need the end result to be multiple files which I don't believe GZipStream supports. Jun 20, 2013 at 16:09
  • OK, then try SevenZipSharp. It's built around the 7-zip library, but that can compress to a .zip file as well. There may also be similar libraries on Codeplex.
    – Katana314
    Jun 20, 2013 at 17:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.