24

Is it somehow possible to create a ZipArchive from the file(s) in memory (and not actually on the disk).

Following is the use case: Multiple files are received in an IEnumerable<HttpPostedFileBase> variable. I want to zip all these files together using ZipArchive. The problem is that ZipArchive only allows CreateEntryFromFile, which expects a path to the file, where as I just have the files in memory.

Question: Is there a way to use a 'stream' to create the 'entry' in ZipArchive, so that I can directly put in the file's contents in the zip?

I don't want to first save the files, create the zip (from the saved files' paths) and then delete the individual files.

Here, attachmentFiles is IEnumerable<HttpPostedFileBase>

using (var ms = new MemoryStream())
{
    using (var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, true))
    {
        foreach (var attachment in attachmentFiles)
        {
            zipArchive.CreateEntryFromFile(Path.GetFullPath(attachment.FileName), Path.GetFileName(attachment.FileName),
                                CompressionLevel.Fastest);
        }
    }
    ...
}
leppie
  • 115,091
  • 17
  • 196
  • 297
Flair
  • 2,899
  • 5
  • 21
  • 22
  • 4
    Have you tried this http://stackoverflow.com/questions/12347775/ziparchive-creates-invalid-zip-file/12350106#12350106 ? – Angelo Reis Apr 15 '15 at 10:22
  • Why not create a temporary file and then release when the archive has completed? – RussDunn Apr 15 '15 at 10:33
  • I want to avoid creating a temp file and delete it afterwards, if possible. That's what I wrote in the question, but if no other way, then I'll have to do that. – Flair Apr 15 '15 at 10:36

2 Answers2

28

Yes, you can do this, using the ZipArchive.CreateEntry method, as @AngeloReis pointed out in the comments, and described here for a slightly different problem.

Your code would then look like this:

using (var ms = new MemoryStream())
{
    using (var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, true))
    {
        foreach (var attachment in attachmentFiles)
        {
            var entry = zipArchive.CreateEntry(attachment.FileName, CompressionLevel.Fastest);
            using (var entryStream = entry.Open())
            {
                attachment.InputStream.CopyTo(entryStream);
            }
        }
    }
    ...
}
Community
  • 1
  • 1
Alex
  • 13,024
  • 33
  • 62
  • 3
    Worked like a charm, thanks! I used it to create a zip file of a bunch of PDFs I had stored as byte[]'s in the database. No need to get the file system involved at all. The user clicks a button and the .zip file is downloaded through their web browser. (BTW, on the inner-most "using" block I instead used entryStream.Write(arrBytes, 0, arrBytes.Length); – Jason Parker Nov 11 '15 at 22:46
  • 3
    What is attachmentFiles? – Demodave Dec 22 '16 at 22:41
  • 7
    I had to add `ms.Seek(0, SeekOrigin.Begin);` at the end before I could use it. – rob Apr 06 '18 at 21:59
1

First off thanks @Alex for the perfect answer.
Also for the scenario you need to read from file systems :

using (var ms = new MemoryStream())
{
    using (var zipArchive = new ZipArchive(ms, ZipArchiveMode.Create, true))
    {
        foreach (var file in filesAddress)
        {
            zipArchive.CreateEntryFromFile(file, Path.GetFileName(file));
        }
    }

    ...
}

with the help of System.IO.Compression.ZipFileExtensions

Søren
  • 6,517
  • 6
  • 43
  • 47