1
votes

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.

1
What library are you using for the ZipFile? Perhaps try using ICSharpCode.SharpZipLib.Zip. - Bartosz Wójtowicz
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
I used System.IO.Compression.ZipArchive (4.5+) -- you'd CreateEntry(@"dir\file") and Open() that to write to it as a stream msdn.microsoft.com/en-us/library/… and stackoverflow.com/questions/17232414/… - bdimag
@bdimag doesn't that save a temporary file? - TheJediCowboy

1 Answers

0
votes

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