Create zip file from byte[]

2020-01-30 04:21发布

问题:

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?

回答1:

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


回答2:

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!



回答3:

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.