ColdFusion, jQuery, And "AJAX" File Upload Demo

Posted May 28, 2008 at 4:05 PM

Tags: AJAX, ColdFusion, Javascript / DHTML

As part of the project I am currently working on, I had to learn how to post files to the server using AJAX. I had never even attempted this before, so I was extra excited to learn something new. Of course, what you might learn quickly is that you cannot actually do this via "AJAX". Luckily, Rob Gonda warned me about this at the New York ColdFusion User Group when he came to talk about AJAX, and so, I went the "secret iFrame" route.

This technique sounds complicated, but it turns out that with jQuery it is actually quite easy (of course, what DOESN'T jQuery make easy, right?!?). The basic principal is that you hi-jack the form submission process and redirect it to point to a hidden iFrame that you create on the fly. This iFrame then handles the file upload the same way that any ColdFusion page would handle a file upload. Once the files are uploaded, you can either return the data, as I do, or just halt processing.

Ok, let's quickly review the code (I have to get back to work). Here is our XHTML page with the ColdFusion and AJAX demo upload form:

 Launch code in new window » Download code as text file »

  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head>
  • <title>ColdFusion And AJAX File Upload Demo</title>
  •  
  • <!-- Linked scripts. -->
  • <script type="text/javascript" src="jquery-1.2.2.pack.js"></script>
  • <script type="text/javascript" src="ajax_upload.js"></script>
  • </head>
  • <body>
  •  
  • <form>
  •  
  • <h1>
  • ColdFusion And AJAX File Upload Demo
  • </h1>
  •  
  • <input type="file" name="upload1" size="60" /><br />
  • <br />
  •  
  • <input type="file" name="upload2" size="60" /><br />
  • <br />
  •  
  • <input type="submit" value="Upload Files" />
  •  
  • </form>
  •  
  • </body>
  • </html>

Very simple HTML form. What you will notice is that our FORM tag does not have any attributes. These attributes will be manipulated via jQuery once the document object model (DOM) has loaded.

Now, before we get to the cool stuff, let's take a look at our ColdFusion page that will handle the AJAX style file uploads:

 Launch code in new window » Download code as text file »

  • <!---
  • Create an array of files names that we are going to be
  • passing back to the client.
  • --->
  • <cfset arrFiles = [] />
  •  
  •  
  • <!---
  • Loop over form fields looking for files. We dont know if
  • or how many files are going to be uploaded at this point.
  • --->
  • <cfloop
  • index="strFileIndex"
  • from="1"
  • to="10"
  • step="1">
  •  
  • <!--- Build dynamic file field (form field key). --->
  • <cfset strField = "upload#strFileIndex#" />
  •  
  • <!---
  • Check to see if file field exists and that it has
  • value in it (file path).
  • --->
  • <cfif (
  • StructKeyExists( FORM, strField ) AND
  • Len( FORM[ strField ] )
  • )>
  •  
  • <!--- Upload file. --->
  • <cffile
  • action="upload"
  • filefield="#strField#"
  • destination="#ExpandPath( './files/' )#"
  • nameconflict="makeunique"
  • />
  •  
  • <!---
  • Add the generated server file name to the array of
  • file names that we are going to return.
  • --->
  • <cfset ArrayAppend( arrFiles, CFFILE.ServerFile ) />
  •  
  • </cfif>
  •  
  • </cfloop>
  •  
  •  
  •  
  • <!---
  • Create the return HTML. Remember, we are going to be
  • treating the BODY of the returned document as if it
  • were a JSON string.
  • --->
  • <cfsavecontent variable="strHTML">
  • <cfoutput>
  •  
  • <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  • <html>
  • <head></head>
  • <body>#SerializeJSON( arrFiles )#</body>
  • </html>
  •  
  • </cfoutput>
  • </cfsavecontent>
  •  
  •  
  • <!--- Create binary response data. --->
  • <cfset binResponse = ToBinary( ToBase64( strHTML ) ) />
  •  
  • <!--- Tell the client how much data to expect. --->
  • <cfheader
  • name="content-length"
  • value="#ArrayLen( binResponse )#"
  • />
  •  
  • <!---
  • Stream the "plain text" back to the client. It's actually
  • HTML and it is important that we announce it as HTML
  • otherwise the client might not know how to work with it.
  • --->
  • <cfcontent
  • type="text/html"
  • variable="#binResponse#"
  • />

This page takes any form data and searches for form fields that are in the form of "uploadX", where "X" can be a number (in between 1 and 10 in our demo). The ColdFusion template then uploads the files and keeps a running array of the server file names that get generated from the upload process. The key thing to notice is that when the files are done being uploaded, the data is returned as a HTML-wrapped JSON response. This point is very important because it is how our "AJAX" code gets data back as you would from a standard AJAX call.

Ok, now that we have our very basic XHTML file form and our ColdFusion file processing code, it's time to glue them together with a bit of jQuery magic. Here is the jQuery Javascript that hi-jacks the form upload and wires the client and server together:

 Launch code in new window » Download code as text file »

  • // When the DOM loads, initailize the form to upload the files
  • // using an AJAX "like" call rather than a form submit.
  • $(
  • function(){
  • // Get a reference to the form we are going to be
  • // hooking into.
  • var jForm = $( "form:first" );
  •  
  • // Attach an event to the submit method. Instead of
  • // submitting the actual form to the primary page, we
  • // are going to be submitting the form to a hidden
  • // iFrame that we dynamically create.
  • jForm.submit(
  • function( objEvent ){
  • var jThis = $( this );
  •  
  • // Create a unique name for our iFrame. We can
  • // do this by using the tick count from the date.
  • var strName = ("uploader" + (new Date()).getTime());
  •  
  • // Create an iFrame with the given name that does
  • // not point to any page - we can use the address
  • // "about:blank" to get this to happen.
  • var jFrame = $( "<iframe name=\"" + strName + "\" src=\"about:blank\" />" );
  •  
  • // We now have an iFrame that is not attached to
  • // the document. Before we attach it, let's make
  • // sure it will not be seen.
  • jFrame.css( "display", "none" );
  •  
  • // Since we submitting the form to the iFrame, we
  • // will want to be able to get back data from the
  • // form submission. To do this, we will have to
  • // set up an event listener for the LOAD event
  • // of the iFrame.
  • jFrame.load(
  • function( objEvent ){
  • // Get a reference to the body tag of the
  • // loaded iFrame. We are doing to assume
  • // that this element will contain our
  • // return data in JSON format.
  • var objUploadBody = window.frames[ strName ].document.getElementsByTagName( "body" )[ 0 ];
  •  
  • // Get a jQuery object of the body so
  • // that we can have better access to it.
  • var jBody = $( objUploadBody );
  •  
  • // Assuming that our return data is in
  • // JSON format, evaluate the body html
  • // to get our return data.
  • var objData = eval( "(" + jBody.html() + ")" );
  •  
  • // "Alert" the return data (this should
  • // be an array of the server-side files
  • // that were uploaded).
  • prompt( "Return Data:", objData );
  •  
    // Remove the iFrame from the document.
  • // Because FireFox has some issues with
  • // "Infinite thinking", let's put a small
  • // delay on the frame removal.
  • setTimeout(
  • function(){
  • jFrame.remove();
  • },
  • 100
  • );
  • }
  • );
  •  
  •  
  • // Attach to body.
  • $( "body:first" ).append( jFrame );
  •  
  • // Now that our iFrame it totally in place, hook
  • // up the frame to post to the iFrame.
  • jThis
  • .attr( "action", "upload_act.cfm" )
  • .attr( "method", "post" )
  • .attr( "enctype", "multipart/form-data" )
  • .attr( "encoding", "multipart/form-data" )
  • .attr( "target", strName )
  • ;
  • }
  • );
  • }
  • );

There are a lot of comments in this code, which makes it seem big, but there's really only like 10 lines of functional code. I am just trying to be as clear as I can about what is going on. Basically, as I said before, we are taking over the form submission process and pointing it towards an iFrame that we are creating on the fly. Once the iFrame is done processing, it will contain the HTML/JSON response that we created via ColdFusion. This response is then extracted via the iFrame load() event listener (wired via jQuery) and evaluated (converting the JSON data into actual Javascript objects). Then, for our demo purposes, we are simply alerting the file names that we created.

Well, that's all there is to it. A little ColdFusion, a little jQuery, and suddenly, uploading files using an AJAX-like methodology is quite easy, and surprisingly fast.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Permalink  |  Other Searches  |  Print Page




Learning ColdFusion 9 - ColdFusion 9 tutorials, samples, examples, demos

Reader Comments

May 29, 2008 at 5:17 AM // reply »
1 Comments

Thanks for posting the "secret iFrame" route it works like a charm!


May 29, 2008 at 7:26 AM // reply »
5,406 Comments

@Janet,

Glad you liked.


May 29, 2008 at 1:59 PM // reply »
5 Comments

Pretty nice man ... I tend to use the flash route, using swfupload (http://swfupload.org/), but I like the plain jquery solution ... Good one.


May 29, 2008 at 2:02 PM // reply »
5,406 Comments

@Rob,

I am thinking of trying the SWF upload next because I heard you can rock out a "Upload Progress" bar for that :)

Thanks again for the AJAX presentation up here in NYC. It definitely got me excited about trying to make my applications more "AJAXy" in the DHTML sense.


Drew
May 29, 2008 at 7:36 PM // reply »
1 Comments

I fiddled with jQuery plugin AjaxFileUpload until it worked. It doesn't work great with CFCs, but I'm able to upload and manipulate uploaded images.

http://www.phpletter.com/Our-Projects/AjaxFileUpload/

Thanks for posting an alternate method


harvey
Jun 16, 2008 at 2:56 PM // reply »
1 Comments

I was unable to get this to work. Each time it generated an error that the .js file was improperly parenthesized on line 53 - the Prompt call.

I took out the leading $( and ending ); and it worked without errors, but the file is nowhere to be found (already looked in temp).


Jun 17, 2008 at 11:52 AM // reply »
5,406 Comments

@Harvey,

Hmmm. You had the jQuery javascript file included, right?


Jun 25, 2008 at 12:59 PM // reply »
5,406 Comments

@All,

I just found a small bug in IE. Apparently, in IE, you cannot set the enctype attribute of the form. You have to set the "encoding" attribute. I am updating the code to reflect this.


Steve
Jun 27, 2008 at 1:48 PM // reply »
2 Comments

In Firefox 3, after the prompt appears, the browser keeps trying to connect with the site, unlike in IE where it properly just stops. So in FF3 you get a constant wait/loading mouse pointer...


Jun 27, 2008 at 2:06 PM // reply »
5,406 Comments

@Steve,

I get that occasionally, but it seems to be hit or miss. I don't think it affects the functionality at all.


Mahesh
Jul 22, 2008 at 2:10 AM // reply »
3 Comments

Ben very nice! Thank you for the tip.... ! Worked really like a charm. I also referred to Apples famous Remote Scripting Basics and came up with something similar but with the Prototype library. So if someone is looking for that.

File 1: client.php

<form id="form">
<input name="file_upload" id="file_upload" type="file" size="60" />
<span id="upload_update"></span>
</form>

<script type="text/javascript">
Event.observe(window, 'load', function(){
Event.observe('file_upload', 'change', function(){
$('form').writeAttribute({enctype: "multipart/form-data", method:"post", action:"server.php", target:"RSIFrame"});
$('form').insert(new Element("iframe", {id: "RSIFrame", name: "RSIFrame", style: "width: 0px; height: 0px; border: 0px;", src: "blank.html"}));
$('form').submit();
});
});

function showResult(result){
$('file_upload').remove();
$('upload_update').insert(new Element("a", {href: "http://localhost/upload/" + result}).update("Uploaded Image"));
}
</script>

File 2: server.php

<?php
$uploadDir = 'C:/wamp/www/upload/';

$fileName = $_FILES['file_upload']['name'];
$tmpName = $_FILES['file_upload']['tmp_name'];
$fileSize = $_FILES['file_upload']['size'];
$fileType = $_FILES['file_upload']['type'];

$filePath = $uploadDir . $fileName;

$result = move_uploaded_file($tmpName, $filePath);
?>
<script type="text/javascript">
window.parent.showResult('<?php echo $fileName;?>');
</script>


Jul 22, 2008 at 8:17 AM // reply »
5,406 Comments

@Mahesh,

Very cool. I have been playing around a bit with Prototype lately because of some client work and have found it to be very confusing. Granted, the client is using a slightly earlier version of prototype which means that the documentation is not as readily available.

Glad you got this up and running so easily with Prototype. In my experiments, I couldn't even get the dom:loaded event to fire :(


Mahesh
Jul 22, 2008 at 9:55 AM // reply »
3 Comments

Ben the easiest thing to get the latest version of prototype without having to bother about the version is using Google AJAX Library APIs.

Put this into any page and you have the library hooked up with the latest version. Is also available for other frameworks like jQuery and so on.

Check out: http://code.google.com/apis/ajaxlibs/documentation/

These two lines get you Prototypes latest verson 1.6

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript"> google.load("prototype", "1.6"); </script>

Thanks and looking forward to your other posts and if you have any questions on Prototype I can help. Have been using it for a long time.


Jul 22, 2008 at 10:11 AM // reply »
5,406 Comments

@Mahesh,

Thanks man. I have seen some buzz about the Google JS stuff, but have not looked into it just yet.


Mahesh
Jul 22, 2008 at 7:32 PM // reply »
3 Comments

For the above script to work across browsers, do not set the form enctype in the prototype writeAttribute, instead add it to the form, as IE will not do the upload, if the form is set with the encType through JS.


Jul 24, 2008 at 3:16 PM // reply »
5,406 Comments

@Mahesh,

Corrent; I have found that you need to set the "encoding" as well with IE.


Josh
Aug 5, 2008 at 11:22 AM // reply »
1 Comments

Hey there, nice code. I used it as a starting point and converted it to PHP, as well as cleaned up the jQuery a bit. The code is essentially the same however is used for image uploading.

Here's a bit cleaner jQuery:
function gogo(){
$upform = $("form");
$upform.bind("submit", function(event){
$ifrm = $("<iframe name=\"up-" + (new Date()).getTime() + "\" src=\"about:blank\" />")
.hide()
.bind("load", function(event){
$bdy = $(window.frames[$ifrm.attr("name")].document.getElementsByTagName("body")[0]);
alert($bdy.html());
$ifrm.remove();
});
$("body").append($ifrm);
nm = $ifrm.attr("name");
$upform.attr({
action: 'upload.php',
method: 'post',
enctype: 'multipart/form-data',
ecoding: 'multipart/form-data',
target: $ifrm.attr("name")
});

});

The main differences are binding instead of setting (jQuery.click is supposed to be used to trigger a click, jQuery.bind is supposed to be used to bind an action in the event of a click for example), less variables, and the proper setting of attributes. My naming convention generally follows the rule that if the variable holds a jQuery object, start it with a $. ($var as opposed to var).

I don't know if you will use it or not, but hey I thought I'd show my tweaks.


Josh
Aug 5, 2008 at 12:27 PM // reply »
1 Comments

Seems there is either a problem with my script and Opera 9.5 on Linux (Ubuntu 8.04), or with Opera on Linux in general. Opera does not wait until the file is loaded to continue with the script, and so there is no response before the javascript finishes loading. To solve this (which also solves ff3 constantly loading) I added in a window.setTimeout and wrapped it around the contents of the binding to the iframe. General improved syntax:

$ifrm.bind("load", function(event){
window.setTimeout(function(event){
.........
}, 500);
});

Seems less than 500 milliseconds and my short script will not have passed back. I haven't tried on a slower computer but I'd imagine a half a second would be ample time for all scripts to be run.


Aug 5, 2008 at 1:00 PM // reply »
5,406 Comments

@Josh,

I am not sure if there is any difference between using the [object].load() and the [object].bind( "load" syntax. I think one might just act as a short-hand notation for the other. I believe that .load() actually calls .bind() behind the scenes.

Thanks for the tip about the setTimeout() on the load function. I have noticed some continuous loading on FF3 (and sometimes on earlier browsers). I will try putting that into my code.

Thanks.


Aug 5, 2008 at 5:45 PM // reply »
1 Comments

Oh, I don't believe there is anything wrong with it. I just think it is frowned upon standards wise. It might just be me, but I reserve a direct call for when I am instigating a click or something along those lines.

On further look at the jQuery site, it would appear they don't care. Oh well, I'll continue binding anyways.

Cheers
-Josh


Aug 6, 2008 at 1:00 PM // reply »
11 Comments

Awesome example. Thanks Ben...this helped out a great deal.


Aug 7, 2008 at 1:58 PM // reply »
5,406 Comments

@Sam,

No problem man. I will try to implement the code that prevents it from *thinking* for ever post-form submission. If I do, I'll let you know.


Aug 20, 2008 at 1:57 PM // reply »
11 Comments

@Ben: I was merging this with a whole bunch of other JS and things got a little too complicated. So I added a permanent iframe to the page and always post to that (which is fine for my use case) and it solved the "always thinking" issue. Not sure what the cause was but thought that might help.

Lots of people say its good to be always thinking though...


Aug 21, 2008 at 2:27 PM // reply »
5,406 Comments

@Sam,

Hmm, maybe if I don't remove the iFrame, it will help. I don't think there is any harm to letting several IFrames build up, especially when the main page will refresh from time to time.

... it took me several reads to get the "always be thinking" joke :) It's hot in here.


Aug 21, 2008 at 2:46 PM // reply »
5,406 Comments

I updated the example code to have the a slight delay on the frame removal:

setTimeout(
. . . . function(){
. . . . . . . . jFrame.remove();
. . . . },
. . . . 100
. . . . );

This slight delay (could be less than 100, but it doesn't matter) is enough to let FireFox stop "thinking". I am not sure if this would also fix the Opera issue mentioned above.


Evden Eve Nakliyat
Oct 2, 2008 at 2:57 AM // reply »
4 Comments

Can we send text post data with file data?


Steve
Oct 26, 2008 at 8:28 PM // reply »
2 Comments

Ben, how would you integrate progress bar? Any ideas?

Steve


Oct 27, 2008 at 8:12 AM // reply »
5,406 Comments

@Steve,

In order to integrate a progress bar, I think you'd need to use something like SWFUpload. I believe that Flash-based uploading is the only way to get at that kind of information.


rnstr
Nov 12, 2008 at 5:04 PM // reply »
1 Comments

This doesn't seem to work in Google Chrome, too bad. Any thoughts on why not? Not allowed to dynamically add iframes?


Nov 12, 2008 at 5:08 PM // reply »
5,406 Comments

@rnstr,

I find that a lot of AJAX stuff doesn't work in Google Chrome. Not sure why. I've definitely found it to be a hit and miss browser for this sort of thing.


Rick Faircloth
Feb 26, 2009 at 11:34 PM // reply »
6 Comments

Hi, Ben...

I've been trying to implement your code above, but I keep running into an error that was mentioned in the comments, but no resolution was ever mentioned.

After I've browsed and selected two images and click the "Upload Files" button, I get an error in Firebug:

"Missing ) in parenthetical"
<font face="arial"></font>\n

That looks like a problem with the data being returned from "upload_act.cfm". It looks like part of an error message from CF, however, with this style of data handling, there's no "Response" tab for me to click and view the JSON data coming back.

I'm using your code from the .zip file and haven't changed anything, except the names of the files to ajax_upload.js, upload_act.cfm, and form.cfm.

Suggestions on what to look for?

Thanks for the tutorial!

Rick


Rick Faircloth
Feb 27, 2009 at 1:34 PM // reply »
6 Comments

I figured out what the problem was, Ben. The error message, indeed, was code from CF error.

The CF error was occurring because I didn't have the path correct for the folder the files would be written to the in the ajax_upload.js file.

Now I'm trying to figure out the best way to handle a form with filefield(s) and other types of fields.

I guess, upon submission, I could process the filefields, then handle the other fields in another function? I would need to get the names of the uploaded files to put into my database, along with other information.

Would that be a recommended approach?


Mar 2, 2009 at 7:18 PM // reply »
5,406 Comments

@Rick,

Usually what I do, when dealing with complex forms, is to upload the file and return some sort file ID. I then store this ID into the form and then resubmit the whole form using AJAX. So, when dealing with files, I have a two passes:

1. Upload files and store resultant IDs.
2. Send form data (including new IDs) to server using AJAX.


Hoy
Apr 28, 2009 at 5:30 PM // reply »
1 Comments

The demo works fine independently. However I want to bind it into a complex application, I cannot get it work.

The details as follows:

I use <cfmenu> which is in a<cflayoutarea> to invoke a file uploading form in another <cflayoutarea>. then it won't upload files.

I doubt that form submit event in "ajax_upload.js" is not triggered, since the form is loaded only user choosing the menu item lately.

I tried to change variable in ajax_upload.js:

var jForm = $( "form:last" );

but still won't work.

Is that any way we can catch the later added object ?

thanks


Dave
Jun 2, 2009 at 8:37 PM // reply »
8 Comments

Am I the only one that can't get this to work?
the ajax_upload.js file doesn't even seem to be acknowledging that a file is being sent to send as an attachment. sorry if i'm buying ignorant here.


Dave
Jun 2, 2009 at 10:22 PM // reply »
8 Comments

Just kidding.

What I did was put in the absolute path for the secondary page that takes the upload...


Jun 3, 2009 at 11:10 AM // reply »
5,406 Comments

@Dave,

No worries :)


Dave
Jun 3, 2009 at 1:02 PM // reply »
8 Comments

Thanks Ben for the great tips, I've been coming back a bunch.

I just noticed that it doesn't work with CFFORM. I'm trying to attach this to my Mach-II framework as well, and I just started using cflayout and all the new CF8 features, and I guess it's just a matter of figuring out the nuances.

So I'm guessing if there's more than one form here that it won't work as is correct?


Jun 3, 2009 at 5:03 PM // reply »
2 Comments

What if the form is not the first one encountered within a larger CFLAYOUT framework? How do you alter

var jForm = $( "form:first" );

so that it references the correct form? I've tried using both ID and Name with no luck.

Cheers,
Ty


Dave
Jun 3, 2009 at 5:15 PM // reply »
8 Comments

I had another form but used an if else statement to hide it. i also tried to set the frame src dynamically but don't think it liked that too much. i got it to work by itself and update the database table... but i have to doublecheck my code because i want it to redirect to another page afterwards but it's not doing it correctly. well right now it's not working correctly in the cflayout either. ill keep you posted on my progress


Dave
Jun 4, 2009 at 1:12 AM // reply »
8 Comments

Soooo if you have this code in a cflayout tab; it reloads the page (and not the view page that is called via the tab). What's weird is that it's not updating or not able to run the JS code to create the iframe. it's weird because when you submit a form within the cflayout area it goes within itself. I assumed this would do the same for this code but instead it submits it entire page and reloads the url from the browser regardless of tab state.

I'm new to cf8 stuff so I apologize for the ignorance if this has been stated before. i ran the source file called by the tab in a separate window and it works fine.

Can you guys give me some insight or help me with this issue?
Thanks,
Dave


Jun 4, 2009 at 7:50 AM // reply »
11 Comments

@Dave: All cfforms within cflayout or cfdiv will submit via AJAX. Have you tried putting the iframe outside of all the cflayout tags?


Jun 4, 2009 at 7:57 AM // reply »
5,406 Comments

@Dave,

I have not worked with CFForm, so I cannot say at this time why it might not work. Ultimately, it just renders HTML, so it should be ok. However, since CFForm does use Javascript validation, it's possible that the submit event handlers are conflicting.

@Ty,

You just need to come up with some way to identify the form so that it can be referenced later. You could use an ID for example $( "#formID" ).


Dave
Jun 4, 2009 at 9:09 AM // reply »
8 Comments

CFFORM does not work with this workaround for uploads, since it performs it's own JS as Bend said... Does anyone have a clue as to whether they were able to add something to the cfform so it can add to the CF's event handlers?

so basically if i use form instead of cfform it won't stay within the cflayout as well (to sam)? Target of the form doesn't seem to work either but i can't remember if i did that with cfform or not in cflayout. there's gotta be a way around this...


Dave
Jun 4, 2009 at 8:31 PM // reply »
8 Comments

Bah i gave up. I just had a <form> go to a view page not inside a cflayout with the tabs (pretty much the overview page. I just understand why CFGRID fails in IE. Another couple sleepless nights. haha

thanks again guys.


Melemel
Jun 9, 2009 at 12:01 PM // reply »
4 Comments

I must be doing something wrong, because when I try to submit the form and getting nothing. Matter of fact the form all I'm getting is the following in my url:

https://lions/test_dev/form/form.cfm?upload1=C%3A%5CDocuments+and+Settings%5Codthomps.DPMOU%5CMy+Documents%5CDisys%5Ctimesheet_guide.docx

its like everything is submitted via the url and I know that's not what I'm looking for:

I'm using the 4 files required:

1. form.cfm
2. upload_act.cfm
3. ajax_upload.js
4. jquery-1.3.2.min.js

Help needed...I do have one question, how does the form know to communicate with the ajax_upload.js ... without any function trigger...


Jun 9, 2009 at 12:52 PM // reply »
2 Comments

Without seeing how you've implemented the 4 files it's hard to troubleshoot the issue. The only item I had to alter in getting the file upload to work was the destination attribute in the <cffile> tag. The other common problem is having more than one form going. If the upload form is not the FIRST form encountered it will not work.

I can answer your question about how the form communicates with the ajax_upload.js file. Since it is included at the start of the file Form.cfm

<script type="text/javascript" src="ajax_upload.js"></script>

the jForm.submit function "steals/hijacks" control when the submit button is triggered thereby running the js functions and ultimately running the upload_act.cfm file.

Cheers,
Ty


Melemel
Jun 9, 2009 at 1:23 PM // reply »
4 Comments

Ty,

thanks for the quick reply..see code below...maybe you see something I don't...

melemel

----------------------
Form.cfm
---------------------

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>ColdFusion And AJAX File Upload Demo</title>

<!-- Linked scripts. -->
<script type="text/javascript" src="jquery-1.2.2.pack.js"></script>
<script type="text/javascript" src="ajax_upload.js"></script>
</head>
<body>

<form>

<h1>
ColdFusion And AJAX File Upload Demo
</h1>

<input type="file" name="upload1" size="60" /><br />
<br />

<input type="file" name="upload2" size="60" /><br />
<br />

<input type="submit" value="Upload Files" />

</form>

</body>
</html>

--------------------------
upload_act.cfm
--------------------------

<!-- Create an array of files names that we are going to be passing back to the client -->

<cfset arrFiles = [] />


<!-- loop over form fields looking for files. We dont know if or how many files
are going to be uploaded at this point -->
<cfloop index="strFileIndex" from="1" to="10" step="1">

<!-- Build dynamic file filed (form field key). -->

<cfset strField = "upload#strFileIndex#" />

<!-- Check to see if file field exists and that
it has value in it (file path). -->

<cfif ( StructKeyExists( FORM, strField ) AND
Len( FORM[ strField ] ) )>

<!-- Upload File -->

<cffile action="upload" filefield="#strField#"
destination="D:\WWWSite\test_dev\form\"

nameconflict="overwrite"/>
<!-- Add the generated server file name of the array of file
names that we are going to return
-->

<cfset ArrayAppend( arrFiles, CFFILE.ServerFile ) />

</cfif>
</cfloop>

<!--
Create the return HTML. Remember, we are going to be
treating the BODY of the returned document as if it were a
JSON String
-->

<cfsavecontent variable="strHTML">
<cfoutput>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>

<head></head>

<body>#SerializeJSON( arrFiles )#</body>

</html>

</cfoutput>

</cfsavecontent>

<!-- Create binary response data. -->

<cfset binResponse = ToBinary( ToBase64( strHTML ) ) />

<!-- Tell the client how much data to expect -->

<cfheader
name="content-length"
value="#ArrayLen( binResponse )#"
/>

<!-- Stream the "plain text" back to the client. It's actually
HTML and it is important that we announce it as HTML
otherwise the client might not know how to work with it.
-->

<cfcontent
type="text/html"
variable="#binResponse#"
/>

-------------------------------
ajax_upload.js
----------------------------
// When the DOM loads, initialize the form to upload the files
// using an AJAX "like" call rather than a form submit.

$(

function(){
// Get a reference to the form we are going to be
// hooking into.
var jForm = $( "form:first" );

//Attach an event to the submit method. Instead of
// submitting the actual form to the primary page, we
// are going to be submitting the form to a hidden
// iFrame that we dynamically create.
jForm.submit(
function( objEvent ){
var jThis = $( this );

// Create a unique name for our iFrame. We can
// do this by using the tick count from the date.
var strName = ("uploader" + (new Date()).getTime());

// Create an iFrame with the given name that does not point to any
// page - we can use the address
// "about:blank" to get this to happen.
var jFrame = $( "<iframe name=\"" + strName + "\"
src=\"about:blank\" />" );

// We now have an iFrame htat is not attached to
// the document. Before we attach it, let's make
// sure it will not be seen.
jFrame.css( "display", "none" );

// Since we submitting the form to the iFrame, we
// will want to be able to get back data from the
// form submission. To do this, we will have to
// set up an event listener for the LOAD event of
// the iFrame.
jFrame.load(
function( objEvent ){

// Get a reference to the body tag of the
// loaded iFrame. We are doing to assume that
// this element will contain our return
// data in JSON format.
var objUploadBody = window.frames[ strName
].document.getElementsByTagName( "body" )[ 0 ];

// Get a JQuery object of the body so
// that we can have a better access to it.
var jBody = $( objUploadBody );

// Assuming that our return data is in
// JSON format, evaluate the body html
// to get our return data.
var objData = eval( "(" + jBody.html() + ")"
);

// "Alert" the return dat (this should be
// an array of the server-side files
// that were uploaded).
prompt( "Return Data:", objData );

// Remove the iFrame from the document.
// Because FireFox has some issues with
// "Infinite thinking", let's put a small
// delay on the frame removal
setTimeout(
function(){
jFrame.remove();
},
100
);
}
);


// Attach to body.
$( "body:first" ).append( jFrame );

// Now that our iFrame it totally in place, hook
// up the frame to post to the iFrame.

jThis
.attr( "action", "cf_file_handler.cfm" )
.attr( "method", "post" )
.attr( "enctype", "multipart/form-data" )
.attr( "encoding", "multipart/form-data" )
.attr( "target", strName );
}
);
}
);


Dave
Jun 9, 2009 at 1:25 PM // reply »
8 Comments

2. upload_act.cfm

.attr( "action", "cf_file_handler.cfm" )
you need to rename your file upload_act if you haven't already


melemel
Jun 9, 2009 at 1:59 PM // reply »
4 Comments

Hey Dave...thanks for the info...I made the change and save upload_act as "cf_file_handler.cfm" and still no good results...getting the same jargon in the url link.

Any other ideas, Sir....

Melemel


Rick Faircloth
Jun 17, 2009 at 9:28 PM // reply »
6 Comments

I was able to use this in a recently completed site, but now I want to use this in a jQuery modal window. I'm having problems figuring out how to target the modal window.

Anyone tried to use Ben's technique within a modal window?

Since the code is just too long and complex to show here, I'd be willing to pay or buy someone a wish-list gift if you're willing to hook up via Eclipse/CFEclipse through Real-Time Shared Editing, or otherwise if you can't hook up that way.

Anyone accomplished this and willing to work with me to get my code working?


Jun 18, 2009 at 8:36 AM // reply »
5,406 Comments

@Rick,

Are you loading the form dynamically into the modal window?


Rick Faircloth
Jun 18, 2009 at 10:22 AM // reply »
6 Comments

Hi, Ben...

I think showing you the code that opens the Thickbox modal window will answer your question better than I could. The code below is modified to allow me to add some animation (fades) to the showing and hiding of the window.

<script>

function tb_fadeIn() {

var tb_pathToImage = "../images/loadingAnimation.gif";
imgLoader = new Image(); //preload image
imgLoader.src = tb_pathToImage;

tb_show("AddDeal", "../modals/dealAddForm.cfm?height=425&width=500&modal=true", false);

$('#TB_overlay').fadeIn(750);
$('#TB_window').fadeIn(750);

};

</script>

Then, this code prepares the tb_fadeIn function above to be called on document ready:

<script>

$(document).ready(function() {

$('#addDeal').click(function() {
tb_fadeIn();
});

});

</script>

I can get the modal window up, complete with form. I can complete the form, send the results via ajax (minus the filefield being filled in right now) and display those results in an alert, but can't get any ajax response results to show up in the modal window.

I'm just not sure how to target the modal window. I've done this sort of thing via ajax with a login dialog, complete with validation responses, etc., but that didn't involve an image.

I'm trying to set this up so I can preview the form data, including the image file in the same modal window that contains the submitted form.

Just can't quite seem to make the connection.

Thanks for any insight...

Rick


Rick Faircloth
Jun 18, 2009 at 10:23 AM // reply »
6 Comments

Oh, and btw, I love the way you've incorporated your images of yourself with various people into your site...always fresh and interesting...great idea!

Rick


Jun 19, 2009 at 7:08 PM // reply »
5,406 Comments

@Rick,

Because you can alert() the results, it looks like AJAX method is working properly. If you want them to then show up in the modal window, you probably have to inject the returned HTML (I am assuming its HTML you get back).

In that case, you need to get a reference to response container element in the Modal window and inject the HTML. Example:

$('#TB_window').find( "div.ajax-response" ).html( YOUR_AJAX_RESPONSE );

Does that make sense?


Rick Faircloth
Jun 19, 2009 at 11:20 PM // reply »
6 Comments

Thanks, Ben. I'll test out your suggestion to see if I *really* understand. It seems to make sense, but the proof is in the, uh...modal window!


Post Comment  |  Ask Ben

Recent Blog Comments
Secret Admirer
Jul 4, 2009 at 12:23 PM
Project HUGE: Huge In A Hurry - Get Big - Phase 2 / Week 3
My Poor Dreamboat :( I feel so sad when I know you are hurting. I hope you feel better soon. ... read »
Jul 4, 2009 at 9:42 AM
FLV 404 Error On Windows 2003 Server
I bookmarked this page. Thanks for given this great post.... ... read »
Jul 4, 2009 at 4:00 AM
Terms Of Service / Privacy Policy Document Generator
thanks ben, I'm not a big fan of contracts so to find your no no-nesense ToS generator has helped me no end. all the best matt ... read »
Justice
Jul 3, 2009 at 11:10 PM
Create A Running Average Without Storing Individual Values
@Ben, I think you're going about this the wrong way. You're trying to use complicated techniques when there is a simple and beautiful technique readily available (a la Gary Funk's comment). Instead ... read »
Bob
Jul 3, 2009 at 9:19 PM
Project HUGE: Huge In A Hurry - Get Big - Phase 3 / Week 1
a good technical explanation http://crossfitphoenix.typepad.com/crossfit_phoenix_forging_/the-overhead-squat.html ... read »
Jul 3, 2009 at 9:03 PM
Create A Running Average Without Storing Individual Values
If I wanted to do this and only carry two numbers, I'd keep track of the sum and N. Then you are pretty much accurate all the time. average = (sum + new_number) / (N + 1) But all this was in a for ... read »
Roland Collins
Jul 3, 2009 at 8:58 PM
Create A Running Average Without Storing Individual Values
@Martin - not just floating point though. Depending on what langauge you're working in, decimals can cause just as many headaches if they're not precise enough. But again, for most applications, th ... read »
Isnogood
Jul 3, 2009 at 7:16 PM
Project HUGE: Huge In A Hurry - Get Big - Phase 3 / Week 1
Watch this http://www.nsca-lift.org/videos/default.shtml ... read »