The other day, I had an interesting programming problem. I had a series of files in a website, and I needed to give the user the option of downloading them all at once. I didn’t want to have create each of them one by one by hand. Even if I created the zip archive programmatically, I didn’t want to leave the zip file on the file sytem. I wanted to make sure the compressed version did not get out of date and I wanted to avoid taking up unnecessary space on the hard drive. I thought about using the zip library that is part of J#, but I wasn’t sure if I would have to run the install program to get the J# distributable on my development and production servers. That seemed like a hassle. So instead I found SharpZipLib, which is a component to create zip files written in .NET. My code is a variation of some example code for creating the files on fly, except with my version, I never save them to the file system. Instead, I create the archive in memory and streame it directly to the browser. I’ll attach the full source code, but here is the gist of what I did.
First, I retrieve the files that I need from the file system. Next, I instantiate standard .NET MemoryStream object. The MemoryStream lets me create a binary representation of the files in memory. I pass the MemoryStream into the SharpZipLib’s ZipOutputStream object, which represents a zip file:
string[] filesToZip = Directory.GetFiles(Server.MapPath("~/filestozip/"));
this.memoryStream = new MemoryStream();
ZipOutputStream zipStream = new ZipOutputStream(this.memoryStream);
After this, I create a ZipEntry object for each file that I want to put in the archive and populate it with any header information:
string fileName = Path.GetFileName(filesToZip
);
ZipEntry entry = new ZipEntry(fileName);
entry.DateTime = DateTime.Now;
Then I read in each of the files in as a FileStream in the standard fashion. (By the way, I hate streams for some reason. I can never remember how they are supposed to be nested, which ones are abstract and which ones are concrete, and how to use the buffer, etc. But I digress…) After the file is read from the stream it is placed into the ZipEntry object, which in turn is written into the ZipOutputStream:
using (FileStream fileStream = File.OpenRead(filesToZip
) )
{
byte[] buffer = new byte[fileStream.Length];
fileStream.Read(buffer, 0, buffer.Length);
entry.Size = fileStream.Length;
fileStream.Close();
crc.Reset();
crc.Update(buffer);
entry.Crc = crc.Value;
zipStream.PutNextEntry(entry);
zipStream.Write(buffer, 0, buffer.Length);
}
zipStream.Finish();
In the original code example I read, the stream was saved to disk, then closed. I found that closing the stream meant I couldn’t do anything else with it. Instead, I keep it open so I can send it back to the browser, in the same way you would send a file stream. This code should look pretty familiar to most ASP.NET developers who have generated an excel file, PDF, image, etc and sent it to the browser programmatically:
Response.Clear();
Response.AddHeader("Content-Type", "binary/octet-stream");
Response.AddHeader("Content-Length", zip.Length.ToString());
//the filename might be dynamic as well.
Response.AddHeader("Content-Disposition",
"attachment; filename=YourZipFile.zip; size="
+ zip.Length.ToString());
Response.Flush();
Response.BinaryWrite(this.memoryStream.ToArray());
Response.Flush();
Response.End();
Finally, I close and dispose the memory stream. I’m not sure if both are necessary. Usually, Dispose will call Close in most objects (such as DataReaders) but you can’t count on it.
this.memoryStream.Close();
this.memoryStream.Dispose();
That’s it. Below are the links to the SharpZipLib, the original post that I based my code off of, and the download to the full source of this example.
Full Source Code
http://dotnettricks.com/files/4/snippets/entry95.aspx
SharpZipLib
http://www.icsharpcode.net/OpenSource/SharpZipLib/
Creating Zip Files on the Fly
http://weblogs.asp.net/dneimke/archive/2005/02/25/380273.aspx