January 9, 2009
This question crops up fairly often on mailing lists and I usually have to Google the answer so I figured I might as well blog the solution so I can find it more easily (and perhaps so can others).
I'm handling a form post in a CFC but I can't get file upload to work. How do you use <cffile> in a CFC?
The key here is that
<cffile> expects a form field
name for the
filefield= attribute. You'd normally say:
<form enctype="multipart/form-data" ...>
<input type="file" name="userfile" ... />
...
<cffile action="upload" filefield="userfile" ... />
What people often try to do is pass the form fields into the CFC as arguments to a method and then they cannot figure out what to put in the
filefield= attribute.
Inside the CFC, you still need the name of the field in the <cffile> tag when you specify filefield=. The easiest way to do this is to pass the name of the form field to the method:
service.uploadDocument("userfile");
and then inside the method:
<cfargument name="formfield" ... />
...
<cffile action="upload" filefield="#arguments.formfield#" ... />
Note the
#..# there to evaluate the argument to get the string passed in - the form field name.
This is based on Todd Rafferty's post to cf-talk which was the clearest explanation I found via Google!
Comments
You might also mention that you need to add enctype="multipart/form-data" to the form tag. Quite a few people miss that one.
Posted By Michael Long / Posted At
1/9/09 5:21 PM
@Michael, good point. I've updated the post to show that!
Posted By Sean Corfield / Posted At
1/9/09 6:04 PM
Ah, back in the good ol' days when I helped people. ;)
Posted By Todd Rafferty / Posted At
1/9/09 6:19 PM
btw, Sean? The "enctype="multipart/form-data" goes on the actual FORM tag, not the input. :)
Posted By Todd Rafferty / Posted At
1/9/09 6:24 PM
@Todd, thanx. That's what I get for trying to edit the gnarly HTML generated by the code tag in BlogCFC instead of just putting in a new block of code (where I hope I would have noticed that silly error).
Posted By Sean Corfield / Posted At
1/9/09 7:14 PM
No worries, just spotted it and wanted to help out.
Posted By Todd Rafferty / Posted At
1/9/09 8:59 PM
This whole concept is one of those things our team found out the hard way, and I generally accepted with truth without a finely-detailed understanding.
It seems clear that ColdFusion needs to read the multipart/form-data (or one of it's "parts"?) directly from the http stream... am I getting that right? It feels un-encapsulated when I'm making this work properly as you've documented in this post.
So why is it, exactly, that the binary stream can't be, or isn't, passed in as an argument? I'm guessing the answer lies in the way the http POST mechanism works between the browser and the http server.
Posted By Adam Bellas / Posted At
1/10/09 1:48 PM
@Adam: I'm sure that you can pass around the binary stream, but that being said, why would you want to? The binary stream already exists in the form scope, if you duplicate it, then all you're doing is bloating up the memory space within that request? Is it necessary?
I guess you could create a pointer, but at that point, isn't form.whateverfield already enough of a pointer?
Posted By Todd Rafferty / Posted At
1/10/09 4:02 PM
@Todd, I totally see your point. It would bloat memory to pass it around. I guess I'm just trying to keep my OO concepts in order, as I'm somewhat new to OO. The effort to encapsulate everything is what keeps coming to mind.
The way I see it, if you have a CFC that's handling the upload, and it's being called by (let's say we're using Fusebox, it's what I know) an act_handleUpload.cfm, which is called by the framework, which is what was initially hit by the POST request from the browser. Perhaps I've got the encapsulation concepts wrong, but it seems out-of-place to me for the CFC layer to be dependent on anything other than what act_handleUpload.cfm is providing it. The form scope would be one of those "other" things the CFC isn't responsible for knowing. No?
Posted By Adam Bellas / Posted At
1/10/09 5:00 PM
@Adam: I don't consider myself an OO expert, but I would probably file this particular instance as a rule that I'd probably have to break. I'd suggest asking around (perhaps on cf-talk) at what some of the other OO/CF purists are doing.
Posted By Todd Rafferty / Posted At
1/10/09 8:12 PM
@Adam, the way to think about this is that CFFILE uses filefield= in some "magic" way - it is passed a string that identifies the uploaded data. You are passing all the necessary data to the CFC: you're passing the string that identifies the uploaded data. You're not referencing FORM scope in the CFC.
It's not that different to a CFC that accesses some system resource being passed the string that identifies that resource.
Posted By Sean Corfield / Posted At
1/11/09 1:33 PM
Sorry about being late to the party but I just found this entry and it seems to partly address my problems. I'm working on a form that will upload multiple files to my server and I suspect I'll need to call service.uploadDocument in a loop of some kind in my CFC. Is that the right approach?
Posted By Joe Tseng / Posted At
4/15/09 10:05 PM
Post Your Comments
Hosting provided by