Lately, I have come across an increasing number of posts on the Adobe ColdFusion Forums, whose subject I think would result in a nice utility or great learning exercise. Typically, when I get inspired to write a little utility, it's ColdFusion stuff I know but rarely use. A forum post earlier this month spawned another, mostly useless :), utility for zipping and delivering a set of files and/or directories.

[I would have posted this much sooner but broke a wrist on May 18. Since I'm typing one-handed, things are moving slower and my post and examples might not be as detailed as they should...sorry!]

Zipper is a collection of four CFCs and a log file, which, creates a Zip archive from a specified collection of files and/or directories. Once the archive is created, Zipper will deliver it to the requester in one of three ways: (1) downloading to the user's system (prompting the user to save the file, of course); (2) saving it to disk (the server) and returning a download link to the archive, and (3) saving the file to disk (server) and emailing it to a specified recipient.

In instances (1) and (3), the zip archive is deleted from the file system (server) upon completion.

NOTE: I used CFTHREAD to delete the archive from the file system/disk when it's being delivered to the browser for download. CFTHREAD was the best approach for deleting a file served by the browser because, without it, the archive would not delete. As a result, ColdFusion 8 or Railo 3.1 is required (earlier versions of Railo might work -- tested on 3.1).

ZipperBean.cfc -- The properties and getters/setters for Zipper
Zipper.cfc -- Extends ZipperBean, which was done to keep file size down and make development/coding easier
EmailVerifier.cfc -- My Email Verification utility, used to verify an email address when sending an archive by email
Logger.cfc -- A simple logger CFC for recording errors
zipper.log -- A default log file

Installation is pretty simple, drop the above four files into the same directory in your site. I use a directory called 'zipper'. You can, if it's easier or better for your development and/or production environments, place in a directory that is globally accessible in all applications via a CF Administrator mapping.

Technically, Zipper doesn't require any configuration, it only requires an array of file/directory paths (must be absolute paths) sent as a parameter to the zip method.

Zipper will try to help you by setting default storage locations and URL paths based on the calling template's directory but that doesn't always work as expected or desired. As such, you should probably set a few properties (path to save archives, for example) to be sure it runs as expected.

Following is a sample usage of Zipper on a form processing script:

// this script assumes a form has been submitted to it.
// That form contains a series of checkboxes with same id (filesToDownload) whose individual values are full paths to directories or files)
// store files & directories for zipping in an array
if( StructKeyExists( form, "filesToDownload" ) )
files_to_zip = ListToArray( form.filesToDownload );
files_to_zip = ArrayNew(1);

// set up local path variables based on OS and directory of calling template.
// Not required but I like such variables b/c I can move b/w environments with ease (read: no recoding!)
slash = IIf( contains "windows", De( '\' ), De( '/' ));
thisDir = ExpandPath( '.' ) & slash; // current directory but has no trailing slash, so we add it

// instantiate the zipper object -- the path is based on the zipper folder being either at the web root level or a CF Admin mapping.
zipper = createObject( 'component', 'zipper.Zipper' ).init();// this returns an instance of the Zipper object

* configure zipper: all props are optional & are accessed with getter/setter methods (i.e., getProperty_name())
* props (defaults) // description
* files_to_zip (ArrayNew(1)) // the full-path refs to files and/or directories to be zipped
* zip_archive (CreateUUID() & ".zip") // a default archive name, which you may want to set yourself!
* delivery_mechanism ("disk"); // browser, email, or disk
* save_location (ExpandPath( '.' ) & slash) // where to save an archive on the disk, which you may want to set yourself!
* slash ("\") // OS-specific path separator
* log_file (ExpandPath( '.' ) & slash & "zipper.log") // default log file
* save_url ("/archvies/") // url for retrieving archive on disk, assumes a folder called archives exists at the web root (or CF mapping), which you may want to set yourself!
* recursive (true) // include subdirectories when passing in directory paths
* email_sentby ("") // email from
* email_subject ("Requested Files") // email subject
* email_message ("The files you requested are attached in the .zip format.") // email message
* email_recipient ("") // email recipient

// configure instance of Zipper
zipper.setSave_url( '/archives/' );
zipper.setSave_location( thisDir & 'archives/' );
zipper.setLog_file( thisDir & 'zipper/zipper.log' );
zipper.setDelivery_mechanism( 'disk' ); // default is 'disk' but here for example
// zip the files
result = zipper.zipFiles( files_to_zip );

// output result - delivery_mechanism is disk, so it's either a URL or 'failure'
if(result is not "failure"){
response = "Download the Archive";
response = "WTF?!? No file to download.";

zip() Result:
Because Zipper does one of three things with the created archive (delivers by email, delivers to the browser, or creates a link), it wasn't possible to use a simple boolean value to indicate failure or success.

A string is returned and contains one of several values based on the delivery mechanism. In all cases, the string 'failure' is returned if the execution, well, fails!

Save Archive to Disk
Returns a URL-formatted string to the archive (to download).

Send Archive in Email
Returns the string 'email' to indicate it was delivered via email.

Send Archive to Browser for Download
Returns the string 'browser' to indicate it was delivered to the browser.

Where Da Code At?
I'm actively (no, really, I am) rebuilding my two business sites. For now, I have a project page at my ImageAid site (running Railo 3.1 on Tomcat 6.0.18). It's very dull (no design yet) but has a list of my open-source projects which you can select and download. This section is under current development as well, meaning examples and documentation are still to come.

Of course, the form processing uses Zipper to create the archives on the fly :)!
Get the Code


Craig Kaminsky
I finally got a second to properly setup my S3 bucket. The files are located here:

I would also like a copy of these files. Are they posted somewhere?
Did this ever get made live?
Craig Kaminsky
Ugh! I had to move my site. Sorry about that. If you subscribed to the comments, I'll try to get this online today or tomorrow so you can use it, should you like.

Again, sorry about that!
The 'Get the code' link to www.imageaid/projects doesn't work for me. It won't allow me access.... Tant pis!