ASP.NET - Zip Selected Files and Add Files in Memory with DotNetZip

Recently in translating and improving some older JSP - servlet code into ASP.NET, I needed to provide an option to create some report files from database queries, zip them, and serve the resulting zip file to the user in their browser.

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.

By Peter Bromberg   Popularity  (5101 Views)