kuujinbo_dot_info

Using DotNetZip to Send Multiple Files / Downloadable Zip Within ASP.NET

Posted 2011-03-10.

Recently refactored the iText in Action, 2nd Edition Webified iTextSharp Examples to use the DotNetZip Library so each individual example includes all the input and ouput files together. Learning the API, which is incredibly simple, has also helped me a lot at work. Strange and perfect timing, but lately a few of my customers have requested batched reports in various formats.

Quickstart Documentation

Can be found here. Although the code snippets are quite short you can get a lot done with them. The first paragraph recommends always declaring and instantiating the ZipFile object within a using block, since it implements the IDisposable interface.

Adding Items to the Zip Archive

The next thing you should immediately notice is that the library give you many different ways to add individual items to the zip archive. For example, you can add strings, any type of Stream, or a byte arrays.

A Minor Problem (for me)

On a recently completed project one of the requirements was to create an archive of HTML files with both English and Japanese. Being the lazy developer, I assumed that the library took care of eveything for me. After all, in .NET a string is always sequence of characters in Unicode encoding. So after building the HTML files using StringBuilder, I called the two parameter AddEntry() method, since it always worked in the past. But it didn't. My lame way to correct the problem was to call Encoding.UTF8.GetBytes(), passing the result of StringBuilder.ToString(). Writing this article I decide to put a little more thought into the problem, and wouldn't you know there's actually a three parameter override to AddEntry(); you pass the correct Encoding as the third parameter. Duuuuuuuuuh! One thing to note from the documentation, however, is that with the three parameter overload "No Byte-order-mark (BOM) is emitted into the file". So there actually may be times when you need to call Encoding.GetBytes() or one of it's derivatives like I tried :)

The Code

So here's a short but sweet example where a byte array and string are added to the zip archive:

// the string we put in the text file of the zip archive  
  string nihongo = "日本語";
  Response.Clear();
  Response.BufferOutput= false;  // for large files
  Response.ContentType = "application/zip";

  using (ZipFile zip = new ZipFile()) {
/*
* user chose 'Set encoding' option from <input type='radio' />
* the text file will be displayed properly
*/
    if (stringTest.SelectedIndex == 1) {
      Response.AddHeader("content-disposition", "filename=good.zip");
/* 
* my initial / alternate way to set the encoding
* 
*      zip.AddEntry("good.txt", Encoding.UTF8.GetBytes(nihongo));
*
*/        
      zip.AddEntry("good.txt", nihongo, Encoding.UTF8);
    }
/*
* user chose 'Do NOT set encoding' option from <input type='radio' />
* the text file contains garbage characters
*/      
    else {
      Response.AddHeader("content-disposition", "filename=bad.zip");
      zip.AddEntry("bad.txt", nihongo);
    }
/*
* add a byte array to the zip archive, see code snippet below
*/
    zip.AddEntry("hello.pdf", _getPdf());
    zip.Save(Response.OutputStream);
  }
  Response.Close();

The byte array is standard iTextSharp stuff:

  private byte[] _getPdf() {
/*
 * the using blocks are __ONLY__ supported from 5.0.6!
 */
    using (MemoryStream ms = new MemoryStream()) {
      using (Document doc = new Document()) {
        PdfWriter.GetInstance(doc, ms);
        doc.Open();
        doc.Add(new Paragraph("Hello World!"));
      }
      return ms.ToArray();
    }
  }

Wrapping Up

Don't forget; since we're using DotNetZip and iTextSharp you need to include the following using directives:

using Ionic.Zip;
using iTextSharp.text;
using iTextSharp.text.pdf;

Demo

When trying the demo, note the difference if you don't set the proper encoding on the text file.