The original code was somewhat antiquated and involved creating custom directories
whose names included the SessionID in order to store all the created files prior
to creating the zip file. Of course, the server would then become littered with
these folders and their contents without some way of cleaning up and deleting
old ones. I knew there must be a better way than to have to use the file system.
Then I remembered the DotNetZip library up on Codeplex.com.
This demo ASP.NET app uses the DotNetZip "reduced" assembly (which contains
everything except the SFX portions - which in many cases you won't need) -- which
weighs in at a mere 195K. We will populate a FileListView control with the files
that can be found right in the application root itself. You can then check which
files you want zipped.
It also creates a custom Readme.txt file in memory and stores that in the zip file
without having to save the file first, illustrating how to create and zip a file
in - memory.
Finally the completed zip file is streamed directly to the browser for download.
No folders are created and no files are ever written to disk, making this a much
faster and cleaner approach.
DotNetZip is much simpler to use than say, SharpZipLib or other zip libraries.
Here is the code for the ASPX Markup of the page:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs"
Inherits="DotNetZipDemo.Default" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<h3> <span id="Title" runat="server" />Zip Files in ASP.NET </h3>
<p>This page uses the .NET Zip library to dynamically create a zip archive, and then
download it to the browser through Response.OutputStream</p>
<span class="SampleTitle"><b>Check the boxes to select the files, then click the button to zip them up
and send the zip file via the browser.</b></span><br/>
<br/>
<asp:Button id="btnGo" Text="Zip checked files" AutoPostBack OnClick="btnGo_Click" runat="server"/>
<br/>
<br/>
<span style="color:red" id="ErrorMessage" runat="server"/>
<br/>
<asp:ListView ID="FileListView" runat="server">
<LayoutTemplate>
<table>
<tr ID="itemPlaceholder" runat="server" />
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td><asp:Checkbox ID="include" runat="server"/></td>
<td><asp:Label id="label" runat="server" Text="<%# Container.DataItem %>" /></td>
</tr>
</ItemTemplate>
<EmptyDataTemplate>
<div></div>
</EmptyDataTemplate>
</asp:ListView>
</form>
</body>
</html>
And finally, here is the codebehind for the page:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Ionic.Zip;
namespace DotNetZipDemo
{
public partial class Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
try
{
if ( !Page.IsPostBack )
{
String homeDir = Server.MapPath(".");
String sMappedPath= Server.MapPath(".");
var fqFilenames= new List<String>(System.IO.Directory.GetFiles(sMappedPath));
var filenames= fqFilenames.ConvertAll((s) => { return s.Replace(sMappedPath+"\\", ""); });
ErrorMessage.InnerHtml = "";
FileListView.DataSource = filenames;
FileListView.DataBind();
}
}
catch
{
}
}
protected void btnGo_Click(object sender, EventArgs e)
{
ErrorMessage.InnerHtml = "";
var filesToInclude = new System.Collections.Generic.List<String>();
String sMappedPath = Server.MapPath(".");
var source = FileListView.DataKeys as DataKeyArray;
foreach (var item in FileListView.Items)
{
CheckBox chkbox = item.FindControl("include") as CheckBox;
Label lbl = item.FindControl("label") as Label;
if (chkbox != null && lbl != null)
{
if (chkbox.Checked)
{
ErrorMessage.InnerHtml += String.Format("adding file: {0}<br/>\n", lbl.Text);
filesToInclude.Add(System.IO.Path.Combine(sMappedPath, lbl.Text));
}
}
}
if (filesToInclude.Count == 0)
{
ErrorMessage.InnerHtml += "You did not select any files.<br/>\n";
}
else
{
Response.Clear();
System.Web.HttpContext c = System.Web.HttpContext.Current;
String ReadmeText = String.Format("README.TXT\n\nHello There!\n\n" +
"This is a zip file that was dynamically generated at {0}\n" +
"by an ASP.NET Page running on the machine named '{1}'.\n" +
"The server type is: {2}. This Readme file was generated in memory.\n",
System.DateTime.Now.ToString("G"),
System.Environment.MachineName,
c.Request.ServerVariables["SERVER_SOFTWARE"]
);
string archiveName = String.Format("archive-{0}.zip", DateTime.Now.ToString("yyyy-MMM-dd-HHmmss"));
Response.ContentType = "application/zip";
Response.AddHeader("content-disposition", "filename=" + archiveName);
using (ZipFile zip = new ZipFile())
{
foreach (var f in filesToInclude)
{
zip.AddFile(f, "files");
}
zip.AddEntry("Readme.txt", ReadmeText);
zip.Save(Response.OutputStream);
}
Response.End();
}
}
}
}
The above code should be pretty much self-explanatory. DotNetZip comes with versions
for various Frameworks, and has a nice CHM help file with plenty of good documentation.
You can download the demo Visual Studio 2010 Solution here.