Raymond Camden's Blog Rss

Can you do file uploads with ColdFusion 8's Ajax features?

61

Posted in ColdFusion | Posted on 02-27-2008 | 13,378 views

I've gotten this question about 200 times, as have other bloggers, so I thought I'd write up a quick blog post to discuss it.

The short answer is no. Ajax, specifically the feature used to make HTTP requests, cannot do file uploads. Period. This is a security feature enforced at the browser level.

However there are workarounds. The typical workaround uses JavaScript to create a hidden iframe. A new form is created here and then that form is posted to the server. You can then monitor the result of the form post and handle it.

If you Google, you will find about 2 million examples of this. I spent five minutes and picked an example that was simple and direct. This is - most probably - not the best example - but it worked. For this example I'll be using the code from:

AjaxFileUpload Demo

Yes, that's a PHP site. Get over it. ;) The example makes use of jQuery and runs as a jQuery plugin.

I downloaded the AjaxFileUpload zip. It provided everything. I copied the JS, CSS files, and images over, and then modified his main demo file to come up with the following base code:

view plain print about
1<html>
2<head>
3<title>Ajax File Uploader Plugin For Jquery</title>
4<script src="jquery.js"></script>
5<script src="ajaxfileupload.js"></script>
6<link href="ajaxfileupload.css" type="text/css" rel="stylesheet">
7<script type="text/javascript">
8    function ajaxFileUpload()
9    {
10        $("#loading")
11        .ajaxStart(function(){
12            $(this).show();
13        })
14        .ajaxComplete(function(){
15            $(this).hide();
16        });
17
18        $.ajaxFileUpload
19        (
20            {
21                url:'/testingzone/ajaxupload/doajaxfileupload.cfm',
22                secureuri:false,
23                fileElementId:'fileToUpload',
24                dataType: 'json',
25                success: function (data, status)
26                {
27                    if(typeof(data.error) != 'undefined')
28                    {
29                        if(data.error != '')
30                        {
31                            alert(data.error);
32                        }else
33                        {
34                            alert(data.msg);
35                        }
36                    }
37                },
38                error: function (data, status, e)
39                {
40                    alert(e);
41                }
42            }
43        )
44        
45        return false;
46
47    }
48</script>    
49</head>
50
51<body>
52    
53<div id="wrapper">
54 <div id="content">
55     <h1>Ajax File Upload Demo</h1>
56     <p>Jquery File Upload Plugin - upload your files with only one input field</p>
57        <img id="loading" src="loading.gif" style="display:none;">
58        <form name="form" action="" method="POST" enctype="multipart/form-data">
59        <table cellpadding="0" cellspacing="0" class="tableForm">
60
61        <thead>
62            <tr>
63                <th>Please select a file and click Upload button</th>
64            </tr>
65        </thead>
66        <tbody>    
67            <tr>
68                <td><input id="fileToUpload" type="file" size="45" name="fileToUpload" class="input"></td>    
69
70                    
71            </tr>
72
73        </tbody>
74            <tfoot>
75                <tr>
76                    <td><button class="button" id="buttonUpload" onclick="return ajaxFileUpload();">Upload</button></td>
77                </tr>
78            </tfoot>
79    
80    </table>
81        </form>     
82 </div>
83
84
85</body>
86</html>

You want to pay particular attention to the ajaxFileUpload function. The only thing I modified was the URL value. To handle the file upload on the server, you handle it like any other file upload. However, his code wanted a JSON structure back. Luckily that is incredibly easy in ColdFusion.

view plain print about
1<cfset r = structNew()>
2<cfset r["msg"] = "">
3<cfset r["error"] = "">
4
5<cfif structKeyExists(form, "fileToUpload") and len(form.filetoupload)>
6    <cffile action="upload" filefield="fileToUpload" destination="#expandPath("./")#" nameconflict="makeunique">
7    <cfset r["msg"] = "Your file was #file.clientfile# and had a size of #file.filesize#.">
8<cfelse>
9    <cfset r["error"] = "No file was uploaded.">
10</cfif>
11
12<cfoutput>#serializeJSON(r)#</cfoutput>

So obviously if you use some other method, your code will vary, but hopefully this simple example will be enough to get people started.

If readers would like to recommend their own scripts, please do so.

Comments

[Add Comment] [Subscribe to Comments]

I think what throws people the most is why it wasn't included/integrated with CF8 and the EXT library, either at the time EXT didn't support it or Adobe didn't want to include it. Regardless, it would have been nice if it was there when CF8 was released... mabye next time?
Wow, yet *another* perfectly timed blog post Ray!

I am about to build the user image upload facility on my site which makes heavy use of Ajax. Being able to upload without the need for a 'real' popup window will be very useful.

Thanks for the post - yet again.
I usually hit AjaxRain and search when I need something like this.

Check their category for upload:

http://ajaxrain.com/search.php?seValc=upload

Many choices there - and usually they're pretty good choices.

They also have the Ext widget that Don mentioned (direct link: http://extjs.com/learn/Extension:UploadForm)

I've yet to play with it, but maybe one of the CF/Ext projects will take on simplifying the widget with a custom tag?
And for the record, I've submitted an ER a few months ago for CF next to bake this in...
I can do file uploads with CFDIV using Safari (dont know why it works on Safari)
While CF8 Ajax features are neat, I think it leads to a lot of developers not understanding technically what is really going on with the web page that they are creating. A lot of CF developers still don't know what Ajax is or what it is doing. They think it's some neat CF8 trick, but in reality is just basic web browser functionality.
I personally have elected to hand-code using ExtJS 2.0 and have learned a tremendous amount about Javascript since as well as giving me more flexibilty. Which way is better? As always... It just depends.
There's two ways people generally do this in extjs:

1) flash way (currently somewhat unstable: eg swfupload)
2) hidden iframe submit (via normal form post)

Ext 2.0.2 (out yesterday) now has a standard form submit, however cf8's version is 1.0 so you can use that to submit to a target of an iframe...

1.0 way...
Ext.get('form_id').fileUpload = true;
Ext.get('form_id').dom.submit();

in v2.0.2
Ext.get('form_id').standardSubmit = true;
Ext.get('form_id').submit();

or just add the standardSubmit: true config item and submit the normal extjs way :-)
I have this working fine with a cfm page, but when I put it in a cfc page it complains about:
The cffile action="upload" requires forms to use enctype="multipart/form-data".

The upload goes through fine, but no manner of cftry/cfcatch will prevent this error from being returned. Surely this can be done in a cfc.
How are you using the CFC? Does your form post right to the CFC?
Hi ray!!!

I used your Code above as shown by You. I used the as same and it return the result properly and file got uploaded. I made some changes like:

<html>
<head>
<script src="ajax/jquery.js"></script>
<script src="ajax/ajaxfileupload.js"></script>
<script type="text/javascript">
   function ajaxFileUpload()
   {
      $("#loading")
      .ajaxStart(function(){
         $(this).show();
      })
      .ajaxComplete(function(){
         $(this).hide();
      });

      $.ajaxFileUpload
      (
         {
            url:'doajaxfileupload.cfm',
            secureuri:false,
            fileElementId:'fileToUpload',
            dataType: 'json',
            success: function (data, status)
            {
               if(typeof(data.error) != 'undefined')
               {
                  if(data.error != '')
                  {
                     alert(data.error);
                  }else
                  {
                     alert(data.msg);
                  }
               }
            },
            error: function (data, status, e)
            {
               alert(e);
            }
         }
      )
      
      return false;

   }
   </script>
<link href="style.cfm" rel="stylesheet" type="text/css">
<cfif IsDefined('form.cat_ID')>
   <cfif form.cat_ID EQ 2>
<cfset commitIt = "Yes">
<cftransaction action="begin">
<cftry>
<cfinvoke component="core.cfc.front" method="chknew" argumentcollection="#form#" returnvariable="chkResults"/>
<cfif chkResults.recordcount>
<cfelse>
<cfinvoke component="core.cfc.front" method="addnew argumentcollection="#form#" returnvariable="getResults"/>
</cfif>
<cfcatch type="database">
<cftransaction action="rollback"/><cfset commitIt = "No">
</cfcatch>
</cftry>
<cfif commitIt>
<cftransaction action="commit"/></cfif>
</cftransaction>
<cfif getResults EQ True>
<cfset emsg = "Cool! Your contents Has Been Submitted">
</cfif>
</cfif>
    </cfif>
</head>
<body>
<img id="loading" src="ajax/loading.gif" style="display:none;">
<cfform name="form" action="" method="POST" enctype="multipart/form-data">
<table width="100%" cellpadding="0" cellspacing="0" class="tableForm">
<tr>
<td>Upload Picture File:</td>
</tr>
<tbody>
<tr>
<td><input id="fileToUpload" type="file" size="45" name="fileToUpload" class="input" tabindex="1"></td>
</tr>
</tbody>
<tfoot>

<tr>
<td>Title:</td>
</tr>
<tr>
<td><cfinput type="text" name="title" id="title" tabindex="2">
          <cfinput type="hidden" name="cat_ID" value="2">
          </td>
</tr>
<tr>
<td>Contents:</td>
</tr>
<tr>
<td><textarea name="jcontents" id="jcontents" tabindex="3" cols="30" rows="5" title="Contents"></textarea></td>
</tr>
<tr>
<td><cfinput name="uploadbutton" type="button" class="submitbtn" id="uploadbutton" onClick="return ajaxFileUpload();" value="Upload"></td>
</tr>
</tfoot>
</table>
</cfform>
</body>
</html>

here is the contents of doajaxupload.cfm as:

<cfset r = structNew()>
<cfset r["msg"] = "">
<cfset r["error"] = "">

<cfif structKeyExists(form, "fileToUpload") and len(form.filetoupload)>
<cffile action="upload" filefield="fileToUpload" destination="#expandPath("./")#" nameconflict="makeunique">
<cfset r["msg"] = "Your file was #file.clientfile# and had a size of #file.filesize#.">
<cfset form.uploadfile = #cffile.ServerFile#>
<cfelse>
<cfset r["error"] = "No file was uploaded.">
</cfif>

<cfoutput>#serializeJSON(r)#</cfoutput>

i tried to do a upload but it says:

ajaxFilefunction() is undefined. i use firebug to figure out the error:

and it does not work that way.
Well, if the error says ajaxFilefunction(), then somewhere your code is calling it. I don't see it in the code snippet above, but something is calling it. Use your file search. FYI, I'd probably refrain from such large code pastings on the blog. Makes it a bit hard to read. In the future I'd recommend pastebin if your code is more than 5 lines or so.
I'd be willing to bet there is a call to that function in one of your embedded scripts - can you post those to pastebin and put a link here and maybe we can find the issue?
Sorry Guys, I really don't Know what this PASTEBIN is and Where I find it. But Ok i just paste the error what i am encountering. I just played with the code a little bit but almost the same as above posted:

i recieve and error:

ajaxFileUpload is not defined
onclick(click clientX=434, clientY=375)R%2BKS21...VBg%3D%3D (line 2)
[Break on this error] return ajaxFileUpload();

Also I am Calling the page
using <cfdiv><cfinclude template="page.cfm"></cfdiv>
If you remove the <cfdivs> and have your main logic on a page as is, does it work?
Yes! If I remove that <cfdiv>, it works but errors out!!!.

When i click upload button, the loading image start working and alert box pops up with a message [SyntaxError: syntax error], it does not show any line.

All in all i say it works without <cfdiv>

and how if i wanna make this work within <cfdiv>
You should have more of an error than that. Are you using Firefox and Firebug? If not, do so. Don't use IE.
You'll have to import your scripts on the page that contains the cfdiv content...are you doing that?
Yes i am importing all the tags. well using the <cfajaximport tags="cfdiv">

So as ray suggested i removed that div and same error prompts out. i just copied the same above as it is but still syntax error.
You need to dig more into the error using Firebug.
Ok! Ray i digged around, first off i disabled debugging and it worked somehow, when i click upload, it saif no file uploaded..

then i upload the picture i uploaded the file properly and at exact location.

Thereafter i tried uploading other file like doc file and clicked upload it gave me error :

same syntax error and then i tested in IE it error out same [object error].

only when i try uploading other than specified file like image files
Hey Ray! Once i have uploaded the file, it appears on exact location but when the value<cfoutput>serializejson(r)</cfoutput> is used, where i can see its details and how can i use the value in a form to put image name in database. like:

after cffile i used:

<cfset msg = file.serverfile>

bit confused around it
I'm confused too. So it sounds like you upload is ok now, but now you want to know how to save it to the db? If so - that question is really dependent on your application structure. You have access to all the file details in the cffile struct (please read the CF docs for full details), and you can easily do a db insert with those details. Again though - what you save, and how, is dependent on your site.
Hi ray!!! Well I have been really struggling to make it work, i think i am missing something.

I Just moved my Code Logic Where form values are inserted to doajaxupload.cfm page but it still do not work.

I will not not post code here as it is already posted above and u told not to create a big Mess around Here, Still Here i Post the link where i have posted the changed info:

http://pastebin.com/f74bc0442
What isn't working though?
File gets upload, but it does not transfer the form values to database. i trued using <cfdump var="#form#">, but that interuppts the upload functionality.

So, cause troubling me is:

it does not reach form valus.

and no database insert is performed. it is breaking before cfinvoke,

well i am stuck there
Look at your code after the cfif.

<cfset form.uploadfile = #cffile.ServerFile#>
<cfif IsDefined('form.uploadbutton')>
<cfif IsDefined('form.cat_ID')>
<cfif form.cat_ID EQ 2>

One of these are your problem. You should add <cflog> statements to record the values before the cfifs and see if they match what you think they should.
im using coldbox 2.6.1 and whenever i attempt to upload the file, i am receiving an "unexpected token <" error upon submitting the upload. has anyone else seen this error?
That's a syntax error. The error should contain more info and help you find the file/line with the problem.
Ray, I am using this code and wondering how can I print the results of Cffile result on same page. I embeded the doAjaxFileUpload.cfm in the same page. it is working fine except this issue. My javascript code is in different file and I provided link here(ImageWindow.js)

Here's my code look like
<html>
<head>
<title>Ajax File Uploader Plugin For Jquery</title>
<script type="text/javascript" src="../js/jquery.js"></script>
<script type="text/javascript" src="../js/ajaxfileupload.js"></script>
<script type="text/javascript" src="../js/ImageWindow.js"></script>
<link rel="stylesheet" href="../css/thickbox.css" type="text/css" media="screen" />
<script type="text/javascript">

</script>
</head>
<cfform>
<cfset r = structNew()>
<cfset r["msg"] = "">
<cfset r["error"] = "">
<cfoutput>
<cfif isDefined("Form.fileToUpload") >
<cfif structKeyExists(form, "fileToUpload") and len(form.filetoupload)>
<cffile action="upload" filefield="fileToUpload" destination="#expandPath("./PDF")#" nameconflict="makeunique" result="upload">
<cfset r["msg"] = "Your file was #file.clientfile# and had a size of #file.filesize#.">
<cfdump var="#upload#">
<cfelse>
<cfset r["error"] = "No file was uploaded.">
</cfif>
</cfif>
</cfoutput>
</cfform>
<body>

<div id="wrapper">
<div id="content">
<h1>Ajax File Upload Demo</h1>
<p>Jquery File Upload Plugin - upload your files with only one input field</p>
<img id="loading" src="loading.gif" style="display:none;">
<form name="form" action="" method="POST" enctype="multipart/form-data">
<table cellpadding="0" cellspacing="0" class="tableForm">

<thead>
<tr>
<th>Please select a file and click Upload button</th>
</tr>
</thead>
<tbody>
<tr>
<td><input id="fileToUpload" type="file" size="45" name="fileToUpload" class="input"></td>


</tr>

</tbody>
<tfoot>
<tr>
<td><button class="button" id="buttonUpload" onclick="return ajaxFileUpload();">Upload</button></td>
</tr>
</tfoot>

</table>
</form>
</div>


</body>
</html>
Um, not sure why you embedded it in the same page. If you look at my original code, it is very clear that I posted to _another_ CFM. Now, going from there, you can see I return a msg with the clientfile and filesize variables. If you wanted to return more, you just would. It is as simple as changing the r["msg"] line.
Hi Ray

First off I’d like to thank you for the many great examples on your site. I’ve incorporated several into my own projects over the years. For this example upon successful upload I get a syntaxError: missing ; before statement pop-up msg, in lieu of confirmation of successful upload. I believe the source is from the JSON struct being returned back from CF. Any clues here would be greatly appreciated? I've copied your example code exactly.
When you use Firebug, what do you see in the result?
Silly Gotcha

I figured out my problem. I am prefixing all my serialized JSON with a custom prefix. In order to get this work I simply remove the prefix. Something like this:) #RemoveChars(SerializeJSON(r, "false"),1,2)#
Ah, were you using CF8's built in json-prefix support?
Yes, and as expected that prefix is added to all serializedJSON. Doh:) Thanks again for your commitment to the CF community. You ROCK dude!
Interesting. So I've blogged about the json prefix stuff before. If you JUST use 'native' CF8 Ajax stuff, the client side code will auto-strip the prefix. But - I can see this really confusing you if you turn in on once (it can be enabled at the server), forget, and then switch to jQuery.

Awesome - got fodder for a new blog entry. Thanks Martin.
Hi I have one question. I'm using fusebox framework and I am trying to get the url to point to another directory

url:'act_test.cfm' won't work.

the file test.cfm is in a different directory.

test.cfm snippet

url:'/docs/divs/action/act_test.cfm?action=test',

And it keeps error out with a syntaxerror: Missing ; statement?
We would have to see a lot more of the code. Maybe it makes since to use a pastebin type service and point us to it?
Ray,

I'm struggling with this one. I hope you can clear some things up. I am following your example using the ajaxfileupload.js but it seems I contiue to get the IE dreaded 'object error' and FireFox 'Syntax blah blah'. I am not using a JSON prefix and I assume my error occurs when the script attempts to return the 'serializeJSON(r)'. The file uploads fine but no return is made to the js file I guess? Hoping for a Flash of Genius until your response.
What does Firebug show you?
Nothing that I can see?
You should see some kind of network traffic request/response going on. If you have this online where I can see, I'll take a try.
Strange I can see the 'Net' response of {"MSG":"a","ERROR":""}, in Firebug and this appears to be well formed. Yet it does not return to the 'alert' in the javascript file.
A'ha, kinda hard to include all my debug output in the JSON. I seem to remember reading your article on Adobe about this, sorry for wasting your time other than if there is anyone else out there using the 'Enable Request Debugging Output' setting on a DEV server. Be sure to disable this or you'll get the object error with this example.
Glad you got it - had not had a chance to reply yet.
I used cfdiv with AJAX. caller.cfm calls content.cfm with some dynamic javascript.
But the problem is AJAX strips out javascript automatically. I can't include javascript in
caller.cfm page. How can I include javascript?

Thanks.
You _can_ include JS, but when the content is loaded via AJAX, it must have function declarations. Ie, this is not allowed:

function foo() { ...}

However, you can rewrite them as so:

foo = function() { ... }

And this IS allowed.
Wow! It works! Thank you so much. I've been stuck for this one the whole day. You are great!
Hi there, thanks for this example it works a treat, however what happens if I also want to submit other fields in by form like an <input type="text" name="name"> basically any other form variables just get lost, is there any way of passing these as well over to the URL? Thanks
It looks like this particular plugin supports ONLY sending the file. You will need to find another plugin to support sending additional info. There are probably a few hundred different ones out there now. :)

I've used this one most recently: http://www.uploadify.com/
Thanks, I'll check it out
Hi,
I have the same problem with IE. When i am uploading file it shows "SyntaxError: Syntax error".
Does anybody know how to fix this issue?
thanks for example. i tried to change name of file before upload .<cffile action="upload" filefield="fileToUpload" destination="#expandPath("./#randString#.jpg")#" nameconflict="makeunique"> but it work only one extension.how can i get file ext before upload without using hidden field. #file.ClientFileExt# works after upload
You can't change a filename before you upload. You can, though, make destination dynamic on the extension portion as well.
yes i want to make dynamic extension .can i get form.fileToUpload value with coldfusion functions ?
If you dump the result of the cffile upload, you will see a lot of variables referencing the file that was uploaded. Check that and you should see what you want.
ok thanks i ll do what you say :) but it isnt what i want to do because cffile.clientFileExt works after upload, doesnt it ? i should learn ext before upload
like this;
<cffile action="upload" filefield="fileToUpload" destination="#expandPath("./#randString#.#???form.fileToUpload.ext ???#")#" nameconflict="makeunique">
thanks your replies again
ColdFusion can't get the file name. It's server side. In client side code you CAN get the file name, but you can't set it.
Hi Ray,

Sorry to give you such a blast to the past on this one.

I have been playing with this solution all day and think it really is fantastic. What I have is a standard form which has many different fields all relating to an item on an order.

The user must upload two files to go with each item on the order. What I used your functionality here for was, to take the two uploads and let them load up onto the server while showing the progress loop graphic. Then I load the name of the file (used make unique) into a hidden input on the form and I change some text on the screen saying it was loaded.

The user then completes the rest of the form, hits submit, the details are all loaded into a CF Structure and they are brought back to the page to load their next item. Seems to work REALLY well with two exceptions.

1) if the file is 20mgs or larger, I get an error that says [object Error]. I can't figure out why it is failing based on the file size.

and

2) Because I need to take on two separate file uploads from the user per item they are inputting, I have two separate loading progress images on the page. They are named completely differently and the code is set up to reference whichever is appropriate based on the file being loaded, but every time the second file is added, it seems to trigger both progress images.

Here is the link to my code: http://pastebin.com/2ykLmBPb

Any help would be HUGELY beneficial. Thank you so much Ray!

Dallas
I _think_ the second issue is due to what you are doing with ajaxStart. That method is meant to work with ANY Ajax request, not just one. So you've basically got a global method going on there. You would need to change that up a bit.

As for issue 1, I'd check the CF server logs. Or try using the Firebug/Chrome Dev tools JS console to dump out the error.

[Add Comment] [Subscribe to Comments]