Can you do file uploads with ColdFusion 8's Ajax features?
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:
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:
<html>
<head>
<title>Ajax File Uploader Plugin For Jquery</title>
<script src="jquery.js"></script>
<script src="ajaxfileupload.js"></script>
<link href="ajaxfileupload.css" type="text/css" rel="stylesheet">
<script type="text/javascript">
function ajaxFileUpload()
{
$("#loading")
.ajaxStart(function(){
$(this).show();
})
.ajaxComplete(function(){
$(this).hide();
});
$.ajaxFileUpload
(
{
url:'/testingzone/ajaxupload/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>
</head>
<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>
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.
<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#.">
<cfelse>
<cfset r["error"] = "No file was uploaded.">
</cfif>
<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
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.
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?
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.
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 :-)
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.
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.
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>
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>
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.
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
after cffile i used:
<cfset msg = file.serverfile>
bit confused around it
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
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
<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.
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>
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.
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)#
Awesome - got fodder for a new blog entry. Thanks Martin.
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?
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.
