It’s been a while since I did a “ColdFusion Sample”, so if you’ve forgotten, the idea of this is blog series to demonstrate a ColdFusion tag or feature in a complete, if simple, application. In this entry, I’m going to demonstrate CFZIP. This tag allows for creating, reading, and extracting zip files. In my example I’ll be making use of reading and extracting.
I’ve built a small web application that allows you to upload image.
When you upload a file, the logic is pretty simple. Check to see if it's an image first. If it is, we copy the image to the photos directory, then read in the image and scale it for a thumbnail directory. Checking a file to see if it a valid image as easy as using isImageFile. Resizing is also pretty easy with imageScaleToFit. You can also resize with imageResize, but imageScaleToFit allows you to scale images and keep their proportions.
So that portion isn't too complex. But let's ramp it up a notch. What if we allowed folks to upload both images and zips of images. We need to modify our code to check zips for images and extract them as well. Here's the entire template.
<!--- images and thumbs dir are relative --->
<cfset imageDir = expandPath("./photos") & "/">
<cfset thumbDir = expandPath("./thumbs") & "/">
<!--- used to flag if we uploaded crap --->
<cfset successFlag = false>
<cfif structKeyExists(form, "upload") and len(form.upload)>
<cfset tempDir = getTempDirectory()>
<cffile action="upload" filefield="upload" destination="#tempDir#" nameconflict="overwrite">
<cfset theFile = file.serverdirectory & "/" & file.serverfile>
<cfset images = >
<!--- check for zip --->
<cfif file.serverfileext is "zip">
<cfzip action="list" filter="*.jpg,*.png,*.gif" file="#theFile#" name="files">
<cfzip action="unzip" entryPath="#name#" destination="#tempDir#" file="#theFile#" overwrite="true">
<cfif isImageFile(tempdir & "/" & name)>
<cfset arrayAppend(images, tempdir & "/" & name)>
<cfloop index="theFile" array="#images#">
<!--- create a UUID based name. Helps ensure we don't conflict --->
<cfset newName = createUUID() & "." & listLast(theFile, ".")>
<!--- copy original to image dir --->
<cfset fileCopy(theFile, imageDir & newName)>
<!--- now make a thumb version --->
<cfset imgOb = imageRead(theFile)>
<cfset imageScaleToFit(imgOb, 200,200)>
<cfset imageWrite(imgOb, thumbDir & newName)>
<cfset successFlag = true>
<cfset thumbs = directoryList(thumbDir,true,"name","*.jpg|*.png|*.gif" )>
<meta http-equiv="Content-Type" content="text/html;charset=ISO-8859-1" />
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/1.4.0/bootstrap.min.css">
<link rel="stylesheet" href="jquery.lightbox-0.5.css" type="text/css" />
<cfloop index="image" array="#thumbs#">
<a href="photos/#getFileFromPath(image)#" class="imageList">
<img class="thumbnail" src="thumbs/#getFileFromPath(image)#">
<h2>Upload New Image</h2>
<form enctype="multipart/form-data" method="post">
Image(s) have been uploaded. Thanks!
<cfif structKeyExists(variables, "errors")>
Select image, or zip file of images:
<input type="file" name="upload">
<input type="submit" value="Upload" class="btn primary">
Consider the template as two halves. The bottom half simply handles outputting the images and providing a form. This is just regular HTML although I made use of a jQuery LightBox plugin to make it sexier.
Let's focus more on the top portion. First, we wrap our main handling code in a check for an actual file upload. If the form was submitted and nothing was uploaded, we could provide an error. (In fact, you can see where I made use of an error display in the bottom half, but I ended up not bothering creating any errors.) I send the upload to a temp directory outside of web root. Hopefully we all know why.
I've created an array, images, that will store all the files I'll be copying and creating thumbs with. My code then branches into two sections. If the file was an image, I just add it to the array. If the file was a zip, and note we check the extension, there is no "isZipFile" in ColdFusion, I use the list operation of cfzip to get all the images contained within it. For each one, I extract it, check it again to ensure it really is an image, and then add it to array.
At this point I've got an array of images in a temporary directory. I can then simply loop over it and perform my copy/scale operations. Note the use of createUUID(). This provides a new name for the image and allows me to not worry about overwriting an existing image.
That's it. I'm not going to post a demo for this as I know some jerk will abuse the upload. I did include a zip of the code base though. You should be able to extract this locally under your web root and just play with it.