Adding a preview to CFFILEUPLOAD

This post is more than 2 years old.

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!

Raymond Camden's Picture

About Raymond Camden

Raymond is a senior developer evangelist for Adobe. He focuses on document services, JavaScript, and enterprise cat demos. If you like this article, please consider visiting my Amazon Wishlist or donating via PayPal to show your support. You can even buy me a coffee!

Lafayette, LA https://www.raymondcamden.com

Archived Comments

Comment 1 by Robert Zehnder posted on 3/30/2010 at 4:34 AM

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.

Comment 2 by dave posted on 3/30/2010 at 4:55 AM

you could write it to ram:/// as well and not have to use cfcontent.

Comment 3 by Raymond Camden posted on 3/30/2010 at 5:02 AM

@Dave: It _is_ in ram:///. See the previous entry.

Comment 4 by Raymond Camden posted on 3/30/2010 at 5:03 AM

I hit send too soon. No matter where you write it, if you want to serve up the binary data, you need cfcontent.

Comment 5 by dave posted on 3/30/2010 at 5:57 AM

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....

Comment 6 by Raymond Camden posted on 3/30/2010 at 6:42 AM

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?

Comment 7 by dave posted on 3/30/2010 at 7:00 AM

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.

Comment 8 by Sam Farmer posted on 3/30/2010 at 6:05 PM

Very cool. You could also do Word documents, convert them to pdf with cfdocument and get a thumbnail with cfpdf.

Comment 9 by Raymond Camden posted on 3/30/2010 at 6:07 PM

Absolutely. I should probably do a blog entry on a generic 'preview' service.

Comment 10 by Josh posted on 3/30/2010 at 8:21 PM

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?

Comment 11 by Raymond Camden posted on 3/30/2010 at 8:25 PM

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. :)

Comment 12 by MCM posted on 4/21/2010 at 12:14 PM

@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?

Comment 13 by dave posted on 4/21/2010 at 1:38 PM

@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.

Comment 14 by Simon posted on 3/31/2011 at 3:17 PM

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)

Comment 15 by Raymond Camden posted on 3/31/2011 at 3:22 PM

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.

Comment 16 by bianca posted on 5/25/2011 at 7:16 PM

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.

Comment 17 by Raymond Camden posted on 5/26/2011 at 5:40 PM

Smells like a missing /CFIDE virtual folder.

Comment 18 by Jeff posted on 7/14/2011 at 7:22 PM

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>

Comment 19 by Raymond Camden posted on 7/24/2011 at 6:43 PM

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.

Comment 20 by jeff posted on 7/25/2011 at 1:56 AM

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.

Comment 21 by Raymond Camden posted on 7/25/2011 at 3:43 PM

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.

Comment 22 by Misty posted on 12/12/2011 at 5:05 PM

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

Comment 23 by Raymond Camden posted on 12/12/2011 at 6:27 PM

Sorry - what? Your code is the same as mine, is it not? I've got two # since the code was inside cfoutputs.

Comment 24 by Raymond Camden posted on 12/12/2011 at 6:28 PM

Ah, I can see how that can be confusing - since I didn't post the _complete_ code. Good note there.

Comment 25 by Misty posted on 12/12/2011 at 8:10 PM

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.

Comment 26 by Raymond Camden posted on 12/12/2011 at 9:52 PM

Um - I don't quite understand you, sorry.

Comment 27 by Neal Sparks posted on 4/5/2012 at 11:36 PM

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

Comment 28 by Raymond Camden posted on 4/5/2012 at 11:38 PM

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.

Comment 29 by Neal Sparks posted on 4/5/2012 at 11:49 PM

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;

Comment 30 by Raymond Camden posted on 4/5/2012 at 11:58 PM

Hmm. What if you used fileReadBinary instead?

Comment 31 by Neal Sparks posted on 4/6/2012 at 1:08 AM

fileReadBinary works for gif files, but what about the cfpdf jpeg that is not being recognized as an image file

Comment 32 by Raymond Camden posted on 4/6/2012 at 1:13 AM

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?

Comment 33 by Paresh posted on 8/29/2012 at 1:53 AM

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?

Comment 34 by Raymond Camden posted on 8/29/2012 at 4:32 PM

You need to say *how* it didn't work for you - where and how did it fail.

Comment 35 by Paresh posted on 8/29/2012 at 4:58 PM

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!

Comment 36 by Raymond Camden posted on 8/29/2012 at 5:01 PM

Any reason why you can't use imagegetblob for gifs too?

Comment 37 by Paresh posted on 8/29/2012 at 5:15 PM

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.

Comment 38 by Raymond Camden posted on 8/29/2012 at 6:30 PM

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.

Comment 39 by Raymond Camden posted on 8/29/2012 at 6:32 PM

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...

Comment 40 by Paresh posted on 8/29/2012 at 7:31 PM

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!

Comment 41 by Paresh posted on 8/30/2012 at 2:26 AM

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!

Comment 42 by Paresh posted on 9/2/2012 at 4:37 PM

Is there (hopefully a simple way) to integrate a delete feature? I.e. user can click image to remove it!

Comment 43 by Raymond Camden posted on 9/3/2012 at 6:05 PM

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.