Ben Nadel
On User Experience (UX) Design, JavaScript, ColdFusion, Node.js, Life, and Love.
I am the chief technical officer at InVision App, Inc - a prototyping and collaboration platform for designers, built by designers. I also rock out in JavaScript and ColdFusion 24x7.
Meanwhile on Twitter
Loading latest tweet...
Ben Nadel at CFUNITED 2010 (Landsdown, VA) with:

CFZip, CFZipParam, And Virtual File System (RAM) Directories

Posted by Ben Nadel
Tags: ColdFusion

NOTE: I was not running the latest version of ColdFusion 9. Using the freshest install, this now works. My bad :)

As part of my "Best of ColdFusion 9" entry, I needed a way to download IMAP-based mail attachments. To show off some of the features of ColdFusion 9, I figured I would have CFIMAP save the attachments to the virtual file system (RAM disk - ram://) and them ZIP them up and return the resultant archive to the client. I had a heck of a time getting that to work; eventually, I narrowed it down to what I think is a bug in the interaction between the CFZip tag and the RAM disk - specifically, when it comes to zipping an entire source directory off the RAM disk.

To demonstrate this (and get some feedback as to whether or not this is a bug), I went about creating a ZIP file both from the physical file system and from CF9's virtual file system. As my control case, I created the ZIP archive based on an entire physical directory first:

  • <!---
  • Create a ZIP file of the Files directory located on the
  • physical file system.
  • --->
  • <cfzip
  • action="zip"
  • source="#expandPath( './files/' )#"
  • file="#expandPath( './physical_source.zip' )#"
  • overwrite="true"
  • />
  •  
  •  
  • <!---
  • Read the images from the zip file and output them to the
  • screen (to make sure the full cycle works).
  • --->
  • <cfzip
  • name="fileList"
  • action="list"
  • file="#expandPath( './physical_source.zip' )#"
  • />
  •  
  •  
  • <h1>
  • CFZip - Physical File System
  • </h1>
  •  
  • <!--- Loop over the zipped files to extract and output. --->
  • <cfloop query="fileList">
  •  
  • <!--- Read the image binary from the zip. --->
  • <cfzip
  • variable="imageBinary"
  • action="readbinary"
  • file="#expandPath( './physical_source.zip' )#"
  • entrypath="#fileList.directory##fileList.name#"
  • />
  •  
  • <!--- Write the image to the client. --->
  • <cfimage
  • action="writetobrowser"
  • source="#imageBinary#"
  • height="200"
  • style="float: left ; margin-right: 10px ;"
  • />
  •  
  • </cfloop>

This code is pretty straightforward; I set the source of the CFZip tag to be the entire "./files/" directory. Then, to make sure everything worked as expected, I read each binary image from the zip archive and wrote it to the browser as a visual confirmation of the successful life cycle. When we run the above code, we get the following page output:

 
 
 
 
 
 
CFZip, CFZipParam, And The Virtual File System - Physical File System Control Case. 
 
 
 

As you can see, our control case works like a charm.

Next, I tried to do the same thing using a directory on the virtual file system (RAM disk) as the source directory. The code is basically the same, except for that I had to copy the files to the virtual file system before zipping them:

  • <!---
  • Delete RAM directory in case it exists (I needed this to run
  • the page more than once).
  • --->
  •  
  • <cfdirectory
  • action="delete"
  • directory="ram://cfzip-files/"
  • recurse="true"
  • />
  •  
  •  
  • <!---
  • Create the directory in our RAM disk (we will need to copy
  • the physical files to this RAM directory before we can ZIP
  • them).
  • --->
  • <cfdirectory
  • action="create"
  • directory="ram://cfzip-files/"
  • />
  •  
  • <!--- Copy the physical files to the ram disk. --->
  • <cfloop
  • index="fileName"
  • list="girl1.jpg,girl2.jpg,girl3.jpg"
  • delimiters=",">
  •  
  • <cfset fileCopy(
  • expandPath( "./files/#fileName#" ),
  • "ram://cfzip-files/#fileName#"
  • ) />
  •  
  • </cfloop>
  •  
  •  
  • <!---
  • Create a ZIP file of the cfzip-files directory located on
  • the virtual file system.
  • --->
  • <cfzip
  • action="zip"
  • source="ram://cfzip-files/"
  • file="#expandPath( './virtual_source.zip' )#"
  • overwrite="true"
  • />
  •  
  •  
  • <!---
  • Read the images from the zip file and output them to the
  • screen (to make sure the full cycle works).
  • --->
  • <cfzip
  • name="fileList"
  • action="list"
  • file="#expandPath( './virtual_source.zip' )#"
  • />
  •  
  •  
  • <h1>
  • CFZip - Virtual File System
  • </h1>
  •  
  • <!--- Loop over the zipped files to extract and output. --->
  • <cfloop query="fileList">
  •  
  • <!--- Read the image binary from the zip. --->
  • <cfzip
  • variable="imageBinary"
  • action="readbinary"
  • file="#expandPath( './virtual_source.zip' )#"
  • entrypath="#fileList.directory##fileList.name#"
  • />
  •  
  • <!--- Write the image to the client. --->
  • <cfimage
  • action="writetobrowser"
  • source="#imageBinary#"
  • height="200"
  • style="float: left ; margin-right: 10px ;"
  • />
  •  
  • </cfloop>

As you can see, if you ignore the file-copy at the top, this code is basically the same as the physical file system control case. However, when we run this code, we get the following page output:

.... nothing ....

Absolutely nothing gets output to the screen. Not even a ColdFusion error. However, when I check my Application log in the ColdFusion administrator, I see the following exception:

'' The specific sequence of files included or processed is: C:\ColdFusion9\wwwroot\testing\cfzip\vfs.cfm

That's doesn't really help me at all in terms of useful exception messages. After a bit of trial and error, I found that I could finally create the zip archive if I added some CFZipParam tags to the CFZip parent tag. I could keep the virtual directory as the source of the archive, but the CFZipParam tags had to specify the files that would get included:

  • <!---
  • Delete RAM directory in case it exists (I needed this to run
  • the page more than once).
  • --->
  •  
  • <cfdirectory
  • action="delete"
  • directory="ram://cfzip-files/"
  • recurse="true"
  • />
  •  
  •  
  • <!---
  • Create the directory in our RAM disk (we will need to copy
  • the physical files to this RAM directory before we can ZIP
  • them).
  • --->
  • <cfdirectory
  • action="create"
  • directory="ram://cfzip-files/"
  • />
  •  
  • <!--- Copy the physical files to the ram disk. --->
  • <cfloop
  • index="fileName"
  • list="girl1.jpg,girl2.jpg,girl3.jpg"
  • delimiters=",">
  •  
  • <cfset fileCopy(
  • expandPath( "./files/#fileName#" ),
  • "ram://cfzip-files/#fileName#"
  • ) />
  •  
  • </cfloop>
  •  
  •  
  • <!---
  • Create a ZIP file of the cfzip-files directory located on
  • the virtual file system.
  •  
  • NOTE: Because the CFZip tag does not seem to play well with
  • virtual directories as the source, we need to define the
  • individual files using CFZipParam tags.
  • --->
  • <cfzip
  • action="zip"
  • source="ram://cfzip-files/"
  • file="#expandPath( './virtual_source.zip' )#"
  • overwrite="true">
  •  
  • <cfzipparam source="girl1.jpg" />
  • <cfzipparam source="girl2.jpg" />
  • <cfzipparam source="girl3.jpg" />
  •  
  • </cfzip>
  •  
  •  
  • <!---
  • Read the images from the zip file and output them to the
  • screen (to make sure the full cycle works).
  • --->
  • <cfzip
  • name="fileList"
  • action="list"
  • file="#expandPath( './virtual_source.zip' )#"
  • />
  •  
  •  
  • <h1>
  • CFZip - Virtual File System (+CFZipParam)
  • </h1>
  •  
  • <!--- Loop over the zipped files to extract and output. --->
  • <cfloop query="fileList">
  •  
  • <!--- Read the image binary from the zip. --->
  • <cfzip
  • variable="imageBinary"
  • action="readbinary"
  • file="#expandPath( './virtual_source.zip' )#"
  • entrypath="#fileList.directory##fileList.name#"
  • />
  •  
  • <!--- Write the image to the client. --->
  • <cfimage
  • action="writetobrowser"
  • source="#imageBinary#"
  • height="200"
  • style="float: left ; margin-right: 10px ;"
  • />
  •  
  • </cfloop>

Notice that this is exactly the same as the previous code, except for the addition of three CFZipParam tags. This time, when we run the above code, we get the following page output:

 
 
 
 
 
 
CFZip, CFZipParam, And The Virtual File System - Virtual File System Case. 
 
 
 

Now, the CFZip tag works as expected and interacts properly with the virtual file system.

Is this a bug? It certainly feels like one to me. I have looked around the documentation and I don't see any restrictions on directory-level actions.




Reader Comments

Ooops, as it turns out, I was NOT running the latest version of ColdFusion 9.

After upgrading my install, this now works.

Reply to this Comment

Hey Ben.

Your CF9 was out of date? What version are you running, and where did you find a hotfix? I did not see any updates at all for CF9 on the adobe update support site.

Cheers dude.

-Brian

Reply to this Comment

@Brian,

I didn't hotfix. I just uninstalled and re-installed. I may have still been running a pre-release version.

Reply to this Comment

Post A Comment

?
You — Get Out Of My Dreams, Get Into My Comments
Live in the Now
Oops!
Comment Etiquette: Please do not post spam. Please keep the comments on-topic. Please do not post unrelated questions or large chunks of code. And, above all, please be nice to each other - we're trying to have a good conversation here.