A few weeks ago I blogged an example of a "complete" cffileupload demo. Most examples of this tag are very simplistic and don't reflect what a typical form would consist of - ie both a multi-file uploader along with other form fields. A reader commented that they were curious if this could be combined with some sort of preview. I had recently done a blog entry on a related JavaScript library but today I've modified the CFFILEUPLOAD one to add a preview. As this uses most of the code from the original example, I strongly urge you to read that one first. I'll just be explaining the differences and none of the existing code.
To begin, I modified my cffileupload tag to run JavaScript on every file upload. That was as simple as adding the oncomplete argument:
<cffileupload extensionfilter="jpg,jpeg,gif,png,bmp,tiff,pdf" name="portfoliofiles"
maxfileselect="5" title="Portfolio Images" url="fileupload.cfm?#urlEncodedFormat(session.urltoken)#"
oncomplete="previewfile">
(By the way, I also added PDF to the list of extensions, more on the later.) The previewfile function will be passed an object containing the filename and the result from our server side code that handles uploads. Our demo code uses the same name as the user's file, so there is no need to worry about the file having a different name on the server. Here is what I did with the result:
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
<script>
function previewfile(file) {
var previewHTML = '<span class="previewSpan"><img src="preview.cfm?s=' + file.FILENAME + '"></span>'
$("##previewArea").append(previewHTML)
}
</script>
Pretty complex, eh? All I needed to do was create some simple HTML. I point to a new CFM called preview and pass in the file name. Let's take a look at preview.cfm.
<cfparam name="url.s" default="">
<cfif isImageFile(session.myuploadroot & "/" & url.s)>
<cfimage action="read" source="#session.myuploadroot#/#url.s#" name="img">
<cfset ext = listLast(url.s,".")>
<cfset imageScaleToFit(img,125,125)>
<cfelse>
<cfimage action="read" source="default.jpg" name="img">
<cfset ext = "gif">
</cfif>
<cfset bits = imagegetblob(img)>
<cfcontent type="image/#ext#" variable="#bits#">
So what's going on here? Remember that we use a special folder in the virtual file system for the user's uploads. Because of this we can source a CFIMAGE to the file requested. We can also check to see if the file is a valid image. If it isn't, we can use a default picture instead. The final thing we do is get the actual bits and serve it up via cfcontent. Here is a screen shot of the result. Notice that I could have made this look far nicer.

Hopefully this makes sense and is useful!
Archived Comments
I am writing a multifile upload with jQuery right now, just for the heck of it. Demos like this make me wish I had CF9 in production.
you could write it to ram:/// as well and not have to use cfcontent.
@Dave: It _is_ in ram:///. See the previous entry.
I hit send too soon. No matter where you write it, if you want to serve up the binary data, you need cfcontent.
I didn't see the other post but figured it was just pulling from file.
I have never used cfcontent to show back an image like this, I just upload it then show the location in ram just like a reg hhd location.
I don't have an example on hand of just an image but just made a short clip of a video uploader that uploads the video then converts and grabs a thumbnail img from video and displays it back kinda in this same train of thought but it's after the submission because it's grabbed during the video conversion but could just be as easy if doing just an image.
http://www.deliciouscoding....
Because the image is not in a web root accessible file, I have to use cfcontent to load it up. I could point right to the file - but i point to the variable because for most cases, I'm loading it into an image object and resizing it. (Although normally I'd create the thumbnail once.)
I watched your video - cool stuff. What are you using to handle video conversion? ffmpeg?
I guess I should have read the other post but either way hopefully it gives some insite to someone.
I also upload to a folder outside the webroot then virus scan and rename to a uuid right away then pass it into the ram drive for all the processing so that it can be used like just like this for immediate feedback without being in the webroot and thus not needing cfcontent. Nothing wrong with cfcontent just more overhead. The ram drive is a god send! I can't even begin to tell you how much code I am able to take out of uploads by just using it.
The video conversion is done via Railo's cfvideo tags and yes it used ffmpeg. It's crazy how fast it does the conversion. When you see the thumb pop in everything is already done.
One drawback to ffmpeg though is that it rips out all the metadata so the resulting flv can't supply the player with metadata and things like the scrubber bar won't work. What I do is grab the metadata before the conversion then re-inject it back in after the conversion but you got to install some some other tools into os to do that.
Very cool. You could also do Word documents, convert them to pdf with cfdocument and get a thumbnail with cfpdf.
Absolutely. I should probably do a blog entry on a generic 'preview' service.
Why is the default image a jpg yet the default extension is a gif? Is that a mistake, or is there a reason for that?
It is not a mistake. To ask if it is a mistake is to imply that I can make mistakes, which is simply not true. In fact, I reject the existence of your comment as pure fantasy and this reply as nothing more then a delusional interlude in an otherwise normally dreary day.
In other words - yes - a mistake. :)
@Dave
- what is that admin tool you are using? asside from the uploader function, is that something you've built yourself.
it looks like it's nicely designed and might be useful. what theme is that and is it available for download?
also, what are you using to crop that video on screen? is that cropped live or in post?
@MCM
That admin is just one I made.
The screenie of the video is done during the video conversion process and its part of the Railo video tag. So basically you tell it when to extract the image and where to put it then you can do whatever u want with it. I choose to show it back with the thumb but you could certainly do anything with it at that point.
Is there a way using javascript to clear the list of files, im using jquery to show and hide the file upload in a dialog box, but I dont want the previously uploaded file in the list when they click to uplad another file,
thanks :0)
I just checked the JS API for this control and it does not look to be supported. You can clear all files, just not _a_ file.
Hi,
if I try to use cffileupload on our production server nothing shows up, like there was nothing on the page. If I put simple text I see the text but not the supposed interface. Server is using CF9.0
I just used the exemple on the adobe help section.
Smells like a missing /CFIDE virtual folder.
I am confused on your example below how are you getting the file.filename? I am getting undefined. I am currently using this just for one file, and runing a js after all uploaded, I want to add to the form a hidden form value that contains the filename so when the page is submitted I have that on the action page.
function previewfile(file) {
4 var previewHTML = '<span class="previewSpan"><img src="preview.cfm?s=' + file.FILENAME + '"></span>'
5 $("##previewArea").append(previewHTML)
6 }
7 </script>
What does Firebug or Chrome Dev tools tell you? You should inspect what _is_ passed to the function. This is an event handler for the file upload function and result data is passed to it. If the file object is blank or contains other info, it would be useful.
I got this to work after trial and error. Now I have an issue that I am running a javascript after all the uploads to validate and submit the form. My issue is if the validation fails, cffileupload already has the file on the server, the user has to fix the error clear the upload and submit to and the file uploads again. Do you have any ideas how to make this more user friendly? My only thought is to split the js and do the validation on a mouse over the div or something.
Well, you won't know if there is an issue till upload, right? CF provides a few APIs to the file upload control so you can clear it, for example.
Just a Side note ray!
With this $("##previewArea").append(previewHTML)
we can do it as
$("#previewArea").append(previewHTML) and then define the
<div id="previewArea"></div> at the below
and it will work. I am also trying to add the lighbox effect to the generated images to see how it can work, once done i will share the code
Sorry - what? Your code is the same as mine, is it not? I've got two # since the code was inside cfoutputs.
Ah, I can see how that can be confusing - since I didn't post the _complete_ code. Good note there.
Hi Ray, One more thing, Just want to have clarification. you have used the cfcontent tag to display the images, since the images the stored in ram, as per your last complete example of multiupload functionality you are passing the sesssion token in the url. Can i know the bug has been fixed in CF 9.0.1 or still in work in Progress as can i know the Bug ID.
Um - I don't quite understand you, sorry.
you said this "I should probably do a blog entry on a generic 'preview' service." any chance of that 'Generic Preview' need to preview gif and pdf files not just jpg
Gif should work fine already with my code above. For PDF, you can use CFPDF to generate a thumbnail of page 1. I've blogged on that before. It isn't zippy, but would work.
doesn't like gif this line;
<cfset bits = imagegetblob(img)>
causes this error;
An error occured in converting the image to base64 in the required image encoding. Base64 images can be created with ColdFusion in BMP,JPEG,PNG,PNM,and TIFF formats.
Also if I try and to convert the pdf to a jpg thumbnail with cfpdf It does not recognize it as image and displays the default;
Hmm. What if you used fileReadBinary instead?
fileReadBinary works for gif files, but what about the cfpdf jpeg that is not being recognized as an image file
cfpdf jpeg? Do you mean PDFs? Was my comment there not clear? Have you seen how to use CFPDF to generate JPGs from a PDF file?
Hi Ray
Nice work! Just tried fileReadBinary for gif files like this:
<cfif isImageFile(session.destdir & "/" & url.s)>
<cfset ext = listLast(url.s,".")>
<cfimage action="read" source="#session.destdir#/#url.s#" name="img">
<cfif ext NEQ 'gif'>
<cfset imageScaleToFit(img,125,125)>
<cfset bits = imagegetblob(img)>
<cfelse>
<cfset ext = #ext#>
<cfset bits = fileReadBinary(img)>
</cfif>
</cfif>
<cfcontent type="image/#ext#" variable="#bits#">
It didn't work for me unless I have done something wrong!
Any ideas?
You need to say *how* it didn't work for you - where and how did it fail.
Hi Ray
Sorry for partial message. I'm allowing images only to be uploaded. This works fine for jpg and png. Thumbnails are created and displayed correctly. For gif's, only a placeholder is shown! I can't get gif images to display.
Try this test link and you'll see what I mean.
http://www.turn2cash.co.uk/...
Thanks for sharing this code!
Any reason why you can't use imagegetblob for gifs too?
Ray
I've changed the code to this:
<cfif isImageFile(session.destdir & "/" & url.s)>
<cfset ext = listLast(url.s,".")>
<cfimage action="read" source="#session.destdir#/#url.s#" name="img">
<cfset imageScaleToFit(img,125,125)>
<cfset bits = imagegetblob(img)>
</cfif>
<cfcontent type="image/#ext#" variable="#bits#">
The results are still the same! Just can't get the gif images to display.
Ah you are correct. If you view the file directly, you see CF doesn't like Gifs when used this way. So... give me a sec.
Odd - I used the same code as you and it worked. You said a placeholder is showing. I assume that means a broken image. Right click and do "View image in new tab" (or whatever) and you should see a proper error. For comparison, here is the code I used:
https://gist.github.com/351...
Got it Ray! I feel like a fool for not spotting it before! I wasn't prociding the full path to the gif image. Made the changes and it works fine now! All I need to do now is to find a way to resize the gif image to match the other images.
Thanks Ray!
For those that have cfx_imagecr3 installed, here's how to create thumbnails for any most types of images including gif, png etc. This script will only resize images if their dimensions are greater than the resize values, otherwise they will be left at their default values. You can also pass in dynamically (on the fly) created directory names, i.e. to allow for multi user environments.
<cfparam name="url.s" default="">
<cfif isImageFile(session.EncryptionKey.path&"/"&url.s)>
<cfx_imagecr3 getimageinfo="#session.EncryptionKey.path#/#url.s#" >
<cfimage action="read" source="#session.EncryptionKey.path#/#url.s#" name="img">
<cfif imagecr.width GT 75 OR imagecr.height GT 75>
<cfx_imagecr3 load="#session.EncryptionKey.path#/#url.s#" save="#session.EncryptionKey.path#/rs_#url.s#" resize="75x75">
<cfset bits = fileReadBinary(session.EncryptionKey.path&"/rs_"&url.s)>
<cfelse>
<cfset bits = fileReadBinary(session.EncryptionKey.path&"/"&url.s)>
</cfif>
<cfcontent type="image/gif" variable="#bits#">
</cfif>
Hope this helps someone!
Is there (hopefully a simple way) to integrate a delete feature? I.e. user can click image to remove it!
You would need to add some UI to each image preview, like a link (X) or a small 'trash' type icon. Add JavaScript to listen to the click event and when fired, you would call code on the server to handle removing the image from the VFS. You would also need to remove the preview image from the DOM.