I was talking to a reader earlier today about ColdFusion 9's new multi-file uploader. I mentioned my earlier blog post which goes into details about the "multiple post" nature of this control, specifically if you have other form fields involved. He came back with an interesting scenario. How would you handle allowing for metadata about each file upload. By that I mean imagine the following: You've got a form with a few basic fields in (name, email, etc), and then you have the multi-file uploader. For each file you upload you want to ask the user to enter data about the file, like perhaps a nicer name. How could you handle that? Here is one simple example that makes use of jQuery. I wrote this very quickly so please forgive the ugliness.
Ok - the code:2 <script>
3 var counter=0
4
5 function handleComplete(res) {
6 console.dir(res)
7 if(res.STATUS==200) {
8 counter++
9 var newRow = '<tr><td><input type="hidden" name="file_'+counter+'" value="'+res.FILENAME+'">'
10 newRow += 'Label the file: '+res.FILENAME+' <input type="text" name="filename_'+counter+'"></td></tr>'
11 $("table#detail").append(newRow)
12 }
13 }
14 </script>
15
16 <form action="test.cfm" method="post">
17 Name: <input type="text" name="name"><br/>
18 Email: <input type="text" name="email"><br/>
19 Attachments: <cffileupload url="uploadall.cfm" name="files" oncomplete="handleComplete"><br/>
20 <table id="detail">
21 </table>
22 <input type="submit">
23 </form>
24
25 <cfdump var="#form#">
And after submitting, note the form data:
I hope this is helpful. Let me know if you have any questions, or improvements, on the technique.
Edit: For the 'nice name' label I used filename_X. That's a poor choice there since file_X is the filename. Just pretend I used something nice like, oh, filelabel_X.
Comment 1 written by Steve Caldwell on 11 December 2009, at 9:36 AM
Comment 2 written by Raymond Camden on 11 December 2009, at 9:38 AM
Comment 3 written by Steve Caldwell on 11 December 2009, at 9:53 AM
<code><cffileupload url="./handlers/add_files.cfm?uuid=#url.uuid#" name="files" oncomplete="alert('IE SUCKS')"></code>
I get the alert when the page first loads. the files _do_ upload in IE now but won't upload in FF, which previously was the only browser which would upload with the oncomplete attribute included.
the alert does NOT re-fire on each upload completion.
this tag is really picky.
Comment 4 written by Raymond Camden on 11 December 2009, at 10:06 AM
Comment 5 written by Steve Caldwell on 11 December 2009, at 1:10 PM
My issue isn't 100% solved but I have narrowed it down some more. I'm trying to load my swf uploader inside of a jQuery ui tab loaded via AJAX. Do you have much experience with the UI tabs and AJAX functionality? When I first load the page then click the tab with the uploader (tab 2) it doesn't show, but when I go click tab 3 then come back to tab 2, it shows, and uploads and js works fine.
Comment 6 written by Raymond Camden on 11 December 2009, at 2:23 PM
Comment 7 written by Steve Caldwell on 11 December 2009, at 2:56 PM
Comment 8 written by Steve Caldwell on 14 December 2009, at 9:22 AM
<cfloop from="1" to="#listlen(form.filecount)#" index="i">
<cfset fileTag[#i#] = {
file = #form['file#i#']#,
type = #form['filetype#i#']#
}>
</cfloop>
Gives me a nice array with a 2 key struct on each row.
How do I loop over each array row and pull out the 'file' and 'filetype' from the struct?
I tried this:
<cfloop array="fileTag" index="i">
<cfquery name="tagit" datasource="foo">
INSERT INTO files (baa_uuid, file_path, file_revision, file_type_id)
VALUES (#url.uuid#, '#fileTag.file[i]#', 1, '#fileTag.type[i]#')
</cfquery>
</cfloop>
but get...
500 java.lang.String cannot be used as an array
Am I heading in the right direction?
Comment 9 written by Steve Caldwell on 14 December 2009, at 11:47 AM
First, up in the handleComplete function, add this to the newrow var:
<input type="hidden" name="filecount" value="1">
Then on the form post page:
<cfloop from="1" to="#listlen(form.filecount)#" index="i">
<cfset fileTag[#i#] = {file = #form['file#i#']#, type = #form['filetype#i#']#}>
</cfloop>
<cfloop from="1" to="#ArrayLen(fileTag)#" index="i">
<cfquery name="tagit" datasource="shebaa">
INSERT INTO files (uuid, file_path, file_revision, file_type_id)
VALUES ('#url.uuid#', '#form['file#i#']#', 1, '#form['filetype#i#']#')
</cfquery></cfloop>
Comment 10 written by Steve Caldwell on 14 December 2009, at 12:34 PM
So I can skip the whole array thing, and just use the length of my hidden form value...duh...
<cfloop from="1" to="#listlen(form.filecount)#" index="i">
<cfquery ...
Comment 11 written by Raymond Camden on 14 December 2009, at 2:09 PM
Comment 12 written by Alex Yohn on 21 December 2009, at 10:12 AM
<cfajaxproxy cfc="upload" jsclassname="jsobj" />
<script language="JavaScript1.2">
function addfile(supfiles){
var upload = supfiles.FILENAME;
var cfcUpload = new jsobj();
cfcUpload.uploadfiles(upload);
}
</script>
The CFC is:
<cfcomponent output="false">
<!--- Record Uploaded File Details --->
<cffunction name="uploadfiles" output="false" access="remote" returntype="struct">
<cfargument name="fileupload" />
<cfquery name="addfile" datasource="#application.dsn#">
INSERT INTO ext_supportfiles
(programid, userid, filename)
VALUES (#cookie.programid#, #cookie.userid#, '#arguments.fileupload#')
</cfquery>
</cffunction>
</cfcomponent>
Comment 13 written by Alex Yohn on 21 December 2009, at 1:24 PM
Comment 14 written by Raymond Camden on 21 December 2009, at 1:26 PM
Comment 15 written by Alex Yohn on 21 December 2009, at 1:29 PM
Comment 16 written by Raymond Camden on 21 December 2009, at 1:36 PM
Comment 17 written by Alex Yohn on 21 December 2009, at 1:43 PM
Comment 18 written by Raymond Camden on 21 December 2009, at 1:46 PM
Comment 19 written by Alex Yohn on 21 December 2009, at 1:52 PM
http://zeus.wvu.edu/support/upload.cfm
Comment 20 written by Alex Yohn on 21 December 2009, at 1:55 PM
<cfajaxproxy cfc="Support.upload" jsclassname="jsobj" />
<cffileupload
name="SupportFiles"
title = "Support Document Upload"
align="center"
BGCOLOR="FFFFFF"
extensionfilter=".jpg,.doc,.docx,.pdf,.xls,.xlsx,.ppt,.pptx,.rtf,.txt"
hideUploadButton="false"
clearbuttonlabel="Clear List"
deletebuttonlabel="Delete"
addbuttonlabel="Add A File"
uploadbuttonlabel = "Click to Upload"
progressbar = "true"
stoponerror = "false"
url = "/support/processupload.cfm"
height="400"
width = "800"
wmode = "window"
onComplete="addfile"
onError="errorissue">
</cffileupload>
<script language="JavaScript">
function addfile(supfiles){
var upload = supfiles.FILENAME;
var cfcUpload = new jsobj();
//alert(upload+' Uploaded Successfully.')
cfcUpload.uploadfiles(upload);
}
function errorissue(supfiles){
alert(supfiles.STATUS+': '+supfiles.MESSAGE);
}
</script>
Comment 21 written by Alex Yohn on 21 December 2009, at 1:55 PM
<cffile action="uploadall" destination="D:\inetpub\wwwroot\Uploads" nameConflict="makeunique" result="supportfiles"
accept="jpg, doc, docx, xls, xlsx, rtf, txt, pdf">
<cfdump var="#supportfiles#">
Comment 22 written by Raymond Camden on 21 December 2009, at 2:07 PM
Comment 23 written by Alex Yohn on 21 December 2009, at 2:15 PM
struct [empty]
**************************************************************************
Comment 24 written by Raymond Camden on 21 December 2009, at 2:23 PM
Comment 25 written by Alex Yohn on 21 December 2009, at 2:27 PM
accept="jpg, doc, docx, xls, xlsx, rtf, txt, pdf">
<cfdump var="#cffile#" output="d:\inetpub\wwwroot\support\dump.txt">
Comment 26 written by Raymond Camden on 21 December 2009, at 2:28 PM
Comment 27 written by Alex Yohn on 21 December 2009, at 2:35 PM
Comment 28 written by Raymond Camden on 22 December 2009, at 8:04 PM
<cffile action = "upload" destination = "#Expandpath('./uploads')#" nameconflict="makeunique">
<cfset str = {}>
<cfset str.STATUS = 200>
<cfset str.MESSAGE = "File Upload Successful">
<cfoutput>#serializeJSON(str)#</cfoutput>
[Add Comment] [Subscribe to Comments]