1

We have a page that users can download media and we construct a folder structure similar to the following and zip it up and send it back to the user in the response.

ZippedFolder.zip
    - Folder A
         - File 1
         - File 2
    - Folder B
         - File 3
         - File 4

The existing implementation that accomplishes this saves files and directories temporarily to file system and then deletes them at the end. We are trying to get away from doing this and would like to accomplish this entirely in memory.

I am able to successfully create a ZipFile with files in it, but the problem I am running into is creating Folder A and Folder B and adding files to those and then adding those two folders to the Zip File.

How can I do this without saving to the file system?

The code for just saving the file streams to the zip file and then setting the Output Stream on the response is the following.

public Stream CompressStreams(IList<Stream> Streams, IList<string> StreamNames, Stream OutputStream = null)
    {
        MemoryStream Response = null;

        using (ZipFile ZippedFile = new ZipFile())
        {
            for (int i = 0, length = Streams.Count; i < length; i++)
            {
                ZippedFile.AddEntry(StreamNames[i], Streams[i]);
            }
            if (OutputStream != null)
            {
                ZippedFile.Save(OutputStream);
            }
            else
            {
                Response = new MemoryStream();
                ZippedFile.Save(Response);
                // Move the stream back to the beginning for reading
                Response.Seek(0, SeekOrigin.Begin);
            }
        }
        return Response;
    }

EDIT We are using DotNetZip for the zipping/unzipping library.

TheJediCowboy
  • 8,924
  • 28
  • 136
  • 208
  • What library are you using for the ZipFile? Perhaps try using ICSharpCode.SharpZipLib.Zip. – Bartosz Wójtowicz May 23 '14 at 17:39
  • I edited my question to show I was using DotNetZip. I have taken a look at SharpZipLib, but I still do not see how to do it. Do you have an example? – TheJediCowboy May 23 '14 at 18:05
  • I used System.IO.Compression.ZipArchive (4.5+) -- you'd CreateEntry(@"dir\file") and Open() that to write to it as a stream http://msdn.microsoft.com/en-us/library/System.IO.Compression(v=vs.110).aspx and http://stackoverflow.com/questions/17232414/creating-a-zip-archive-in-memory-using-system-io-compression – bdimag May 23 '14 at 18:20
  • @bdimag doesn't that save a temporary file? – TheJediCowboy May 23 '14 at 18:33

1 Answers1

0

Here's another way of doing it using System.IO.Compression.ZipArchive

public Stream CompressStreams(IList<Stream> Streams, IList<string> StreamNames, Stream OutputStream = null)
    {
        MemoryStream Response = new MemoryStream();

        using (ZipArchive ZippedFile = new ZipArchive(Response, ZipArchiveMode.Create, true))
        {
            for (int i = 0, length = Streams.Count; i < length; i++)
                using (var entry = ZippedFile.CreateEntry(StreamNames[i]).Open())
                {
                    Streams[i].CopyTo(entry);
                }

        }
        if (OutputStream != null)
        {
            Response.Seek(0, SeekOrigin.Begin);
            Response.CopyTo(OutputStream);
        }

        return Response;
    }

and a little test:

        using (var write = new FileStream(@"C:\users\Public\Desktop\Testzip.zip", FileMode.OpenOrCreate, FileAccess.Write))
        using (var read = new FileStream(@"C:\windows\System32\drivers\etc\hosts", FileMode.Open, FileAccess.Read))
        {
            CompressStreams(new List<Stream>() { read }, new List<string>() { @"A\One.txt" }, write);
        }

re: your comment -- sorry, not sure if it creates something in the background, but you're not creating it yourself to do anything

bdimag
  • 953
  • 8
  • 11
  • you may need some more seeks, I don't know... e.g. in the for if there's a concern that the input streams could be waiting in the middle... or before the return... I don't know best practice when working with streams. Additionally, there's a CopyToAsync if it's clever. – bdimag May 23 '14 at 19:51