Ever since I started using Coldfusion, something about file uploads didn’t feel right to me. Looking inside the form scope containing an upload, then file field contains the full path to a temporary file. That’s our uploaded file right there, waiting for us to do something with it, but don’t go messing with it just yet. Coldfusion would like you to get it in a certain way:
<cffile action="upload" fileField="upload" destintation="c:\files\upload" /> <cfdump var="#cffile#" />
First, I think the action name is a little confusing because the “upload” has already taken place. The file is sitting right there on our server, in a temporary directory with a temporary name. Also, CF is making me move the file out of the temporary directory, kindly renaming it to the original name as it goes, to a chosen location. Thanks but I’d like to know details like the file type, size and original name before I consider moving it somewhere. Otherwise I might have to just delete it anyway, in which case why not just leave it in the temporary directory where it’ll be removed automatically.
There is a way. The secret was there all along, hidden just beneath the surface of our beloved form scope.
What lies beneath…
In my recent activites poking around the underbelly of CF, I’ve gotten into the habit of investigating what Java class I’m dealing with and working from there. In the case of forms, the form scope is our most likely target to start with.
<cfoutput>#form.getClass().getName()#</cfoutput>
This gives us “coldfusion.filter.FormScope”. Our next step is to see what hidden methods might be available to us that we haven’t been told about
<cfdump var="#CreateObject('java', 'coldfusion.filter.FormScope')#" />
form.getTempFiles()
My first guess was “getTempFiles()” since I knew that the upload field would contain a temporary file path to the upload. After creating a little upload template, I uploaded a file and dumped out the result of that function.
<cfif cgi.request_method Eq 'post'> <cfdump var="#form.getTempFiles()#" /> </cfif> <form action="upload.cfm" method="post" enctype="multipart/form-data"> <input type="file" name="uploaded" /><br /> <input type="submit" value="Upload" /> </form>
As you can see, all we get is a structure containing a key named after each upload form field with the value being a “java.io.File” object. Using the getName() method on the file object gave me the temporary file name, not what we were after.
form.getPartsArray()
The “getPartsArray()” method was the only one left that seemed a possibility. I altered the upload template to dump that out instead.
<cfif cgi.request_method Eq 'post'> <cfdump var="#form.getPartsArray()#" /> </cfif> <form action="upload.cfm" method="post" enctype="multipart/form-data"> <input type="file" name="uploaded" /><br /> <input type="submit" value="Upload" /> </form>
Well, well, well… what have we got ‘ere then! ”com.oreilly.servlet.multipart.FilePart” by the looks of it. Worth doing a web search for, so off I went and I came back with the following information:
- com.oreilly.servlet.multipart.FilePart (docs)
- .getContentType() = Returns the content type of the file data contained within.
- .getFileName() = Returns the name that the file was stored with on the remote system, or null if the user didn’t enter a file to be uploaded.
- Parent class of com.oreilly.servlet.multipart.Part (docs)
- .getName() (From the parent class) = Returns the name of the form element that the Part corresponds to.
Result. We can now use .getFileName() to get the original file name and .getName() to tell us the form field it’s associated with, which in turn will let us do a simple form[fieldName] to get the temporary file location.
Wrapping this up
Now we know where everything is, we just need to simplify the means of getting at it. Here is my approach of using a function to return a structure, each key is named after the upload field and the value contains the information.
<cfscript>
function getUploadData() {
var local = {};
local.result = {};
if (cgi.request_method Eq 'post') {
local.uploads = form.getPartsArray();
if (StructKeyExists(local, 'uploads')) {
local.count = ArrayLen(local.uploads);
for (local.u = 1; local.u Lte local.count; local.u++) {
local.info = GetFileInfo(form[local.uploads[local.u].getName()]);
local.result[local.uploads[local.u].getName()] = {
tempFileName = form[local.uploads[local.u].getName()],
originalName = local.uploads[local.u].getFileName(),
contentType = local.uploads[local.u].getContentType(),
filesize = local.info.size,
ext = ListLast(local.uploads[local.u].getFileName(), '.')
};
}
}
}
return local.result;
}
</cfscript>
Finally. We have the upload information and can make informed decisions about the file before we go and move it anywhere using CFFile and its “upload” action. I think this little function would make a useful addition to Coldfusion but it just got shot down pretty quickly when I last submitted a similar request to the CF bug tracker. Maybe this post might help my little cause





Great post! I always have in the back of my mind that ColdFusion is just an extension of Java, but it’s rare that I see the Java portions exposed like this. I suppose the trouble with something like this is that the underpinnings could change in future versions since they are not “officially” supported parts of ColdFusion itself, and code could break at some point if they did change the innards. The technique is great for learning more about how CF works under the hood though. Thanks!
Just something important to keep in mind: the contentType is probably not the actual mime/type of the file, but simply the type based on the file extension. The means a ColdFusion template named “image.cfm” renamed to “image.png” and then uploaded, would report the contentType as “image/png.”
I would love to see CF add the ability to inspect the actual binary file to determine it’s true content. I know you can’t do it for every file, but they could certainly do it for the most common document types.
This kicks ass. @Dan – i wonder if you’re right about that — i’d really love it if you were wrong on that. can someone test that out to confirm?
Being able to inspect the uploaded form information before you actually invoke the CFFile tag would allow people to get around that “load testing” hack from a while back (assuming you didn’t want to explicitly upload to a non-web-accessible intermediary directory).
@Justin,
Thanks. It’s useful sometimes to dig through what is underpinning CF sometimes. You’re right about it being a concern that things might change, especially when it’s something that’s in CF’s bit of Java and not the standard Java stuff. My main hope for this post is that other developers can see what is already possible and pester Adobe for it. It’s not like they have to do much work to provide a similar function as I’ve shown how simple it is to get at that information.
@Dan,
That’s a very good point about the content type. It really should not be trusted as I’m sure I’ve seen it spoofed as well, so the upload would have a .cfm extension and the content type would be image/png. There may be a Java library out there somewhere that’ll identify file formats, something that might be worth looking into and then pestering Adobe for.
@Todd,
Help me annoy Adobe to get it included
@Ben,
Interesting, I haven’t heard of that hack before. One of the reasons I wanted this was because it didn’t feel right, putting the upload somewhere it shouldn’t be just to find out I didn’t want it in the first place.
I recently released a simple cf function that tries to gather the file type from its contents, it’s at http://magicmime.riaforge.org/ . Feel free to add more file types in there – it’s basic at the moment (all the definitions are hardcoded) but I’d welcome feeback.
Currently supports:-
Adobe PDF Document
Microsoft Office Open XML Format Document
Microsoft Office Open XML Format Spreadsheet
Microsoft Office Open XML Format Presentation
Microsoft Office Word 97-2003 Document
Microsoft Office Excel 97-2003 Document
Microsoft Office Powerpoint 97-2003 Document
Graphics interchange format Image
Portable Network Graphics Image
JPEG Image
HTML (Experimental)
@Paul,
Nice work. I didn’t know someone had given this a go in pure CF. I’ve been looking into some Java libraries to do the task for me but it’s always nice to have a way of doing this natively, without loading an extra Jar.
In case it helps, here’s a page of Java alternatives http://www.rgagnon.com/javadetails/java-0487.html
@David,
People were uploading CFM files and then hitting the “expected” location of the CFM file with a load tester to try to invoke it before the underlying code checked it for a valid file type (and deleted it most likely if it was a CFM file). The problem was, in the clock cycle between CFFile and CFIF, the load tester was hitting the server so much that it was actually able to activate the uploaded CFM file before it was deleted. Really brilliant hack!!
That said, if you can check the file before you CFFILE it, then it’s much better.
@Ben,
That’s pretty clever. Probably why I never felt comfortable moving a file somewhere without knowing what it was.
@all
You might want to take a look at my next post where I do some mimetype detection that’s not based on the file extension. I give Paul’s function a go and try out a Java library too.
Just found out that my bug (feature request) I logged for this has been verified and targetted for CF10.0 Beta 1
Fantastic news. I hope they follow the behaviour I’ve established as it’d mean people would be happier using it now
Seems like a lot of code for no real gain.
this is great information, but how can I use it to prevent a user from uploading a file that is too large?
I have a 10 MB file limit set in the cf administrator, and when someone tries toupload a file larger than this , i get a page reset error page. I want to check the file size and if over the limit, tell the user the file is too big, and prevent the cffile upload from occurring, but in this case, it seems I must also prevent write to temporary location, or server will hiccup. Any suggestions?
Problem is, ColdFusion wants to store that form data somewhere. So it’s always going to hit the disk or you use the setting you mentioned and get that odd error as a response, but at least you know the file is never making it to the server fully (that’s a guess, haven’t confirmed this…).
Ideally you could stop the upload at the web server level. If you’re running Apache, take a look at mod_security which can limit the post request size.