ColdFusion 8 brought us the cfzip tag family, which greatly simplified working with zip files in ColdFusion. But, what about those of us who are not yet, for whatever reason, upgraded to CF8? Since ColdFusion has access to the Java API, we can use the classes in the java.util.Zip package to give us a hand.

The list action for the cfzip tag returns a query object containing information about the files in a zip archive:

  • comment
  • compressedSize
  • crc
  • dateLastModified
  • directory
  • name
  • size
  • type
We can replicate the list tag action by using the class.

First we need to instantiate an instance of the ZipFile class:

view plain print about
2variables.zipFile = createObject("java", "");

The ZipFile class writers made iterating through the contents of the zip file simpler by using an enumeration. We can use a while loop to browse each entry in the zip file:

view plain print about
2local = structNew();
3/* Set up a query containing the same columns as cfzip action='list' */
4local.zipContents = queryNew("comment,compressedSize,crc,dateLastModified,directory,name,size,type");
6try {
7    local.zipFile = variables.zipFile.init( javaCast("string", variables.filename) );
8    local.entries = local.zipFile.entries();
10    while (local.entries.hasMoreElements()) {
11        ...
12    }
14catch (any e) {
15    throw(e);

The entries() method in the ZipFile class returns the enumeration. Each entry is an instance of the class, which describes the actual entry in the zip file. The hasMoreElements() method is a method on the java Enumeration interface that makes controlling the loop very simple.

Within the loop, we populate the query with information about each file:

view plain print about
2local.entry = local.entries.nextElement();
5querySetCell(local.zipContents, "comment", local.entry.getComment());
6querySetCell(local.zipContents, "compressedSize", local.entry.getCompressedSize());
7querySetCell(local.zipContents, "crc", local.entry.getCrc());
8querySetCell(local.zipContents, "dateLastModified", dateAdd("s", left(local.entry.getTime(), 10), dateConvert("utc2Local", "January 1, 1970 00:00")));
9querySetCell(local.zipContents, "directory", reverse(listRest(reverse(local.entry.getName()), "/")));
10querySetCell(local.zipContents, "name", listLast(local.entry.getName(), "/"));
11querySetCell(local.zipContents, "size", local.entry.getSize());
12if NOTT local.entry.isDirectory()) {
13    querySetCell(local.zipContents, "type", "file");    
15else {
16    querySetCell(local.zipContents, "type", "dir");

Note that we have to do some date/time math in order to get a local date/time for the last modified date of each entry. Java returns the last modified date in an epoch time format, which is measured in seconds since January 1, 1970. We first need to remove the milliseconds from the time then add the resulting seconds to the base epoch date to get the local date. Rob Brooks-Bilson has some good information about epoch time and conversion tips.

Here is all of the code combined into a function for easy reuse:

view plain print about
1<cffunction name="list" access="public" output="false" returntype="query">
2    <cfargument name="filename" type="string" required="true" hint="The absolute path to the zip file.">
3    <cfscript>
4        var local = structNew();
5        variables.zipFile = createObject("java", "");
6        local.zipContents = queryNew("comment,compressedSize,crc,dateLastModified,directory,name,size,type");
8        try {
9            local.zipFile = variables.zipFile.init( javaCast("string", arguments.filename) );
10            local.entries = local.zipFile.entries();
12            while (local.entries.hasMoreElements()) {
13                local.entry = local.entries.nextElement();
15                queryAddRow(local.zipContents);
16                querySetCell(local.zipContents, "comment", local.entry.getComment());
17                querySetCell(local.zipContents, "compressedSize", local.entry.getCompressedSize());
18                /* CRC is the checksum for the entry */
19                querySetCell(local.zipContents, "crc", local.entry.getCrc());
20                /*
21                    Java returns the last modified date in an expanded
22                    epoch time format, i.e., milliseconds from 1/1/1970.
23                    We first need to remove the milliseconds from the
24                    time then add the resulting seconds to the base
25                    epoch date to get the local date.
26                */

27                querySetCell(local.zipContents, "dateLastModified", dateAdd("s", left(local.entry.getTime(), 10), dateConvert("utc2Local", "January 1, 1970 00:00")));
28                querySetCell(local.zipContents, "directory", reverse(listRest(reverse(local.entry.getName()), "/")));
29                querySetCell(local.zipContents, "name", listLast(local.entry.getName(), "/"));
30                querySetCell(local.zipContents, "size", local.entry.getSize());
31                if NOTT local.entry.isDirectory()) {
32                    querySetCell(local.zipContents, "type", "file");    
33                }
34                else {
35                    querySetCell(local.zipContents, "type", "dir");
36                }
37            }
38        }
39        catch (any e) {
40            throw(e);
41        }
43        return local.zipContents;