Matt asks:
My question is - is there a simple way to check for nameconflict on cffile action=rename like there ins on cffile action=upload. I have an application where I want to rename whatever they upload to match their order number. It's fine if they just upload one image, but not cool if there is more than one. I'm just checking to see if there is an easier way than some kind of fileexists, then add a 1 to the filename before the rename.
Unfortunately there isn't a simpler way, not that I'm aware of. Your solution is one I used myself very recently. Here is a live working example of code that is run after I used cffile/upload and did security checks. That portion is done and now I just need to copy it to the storage directory.
2
3 <cfif not directoryExists(folder)>
4 <cflog file="picard" text="created a file store for #folder#">
5 <cfdirectory action="create" directory="#folder#">
6 </cfif>
7
8 <!--- before we move our file out of ram and onto disk, need to look for files w/ same name. If so,
9 we prepend N_ in front, going up forever basically --->
10 <cfif fileExists(folder & "/" & getFileFromPath(newFile))>
11 <cfset var keepGoing = true>
12 <cfset var counter = 1>
13 <cfloop condition="keepGoing">
14 <cfset uniqueFileName = folder & "/" & counter & "_" & getFileFromPath(newFile)>
15 <cfif fileExists(uniqueFileName)>
16 <cfset counter++>
17 <cfelse>
18 <cfset dest = uniqueFileName>
19 <cfset keepGoing = false>
20 </cfif>
21 </cfloop>
22 <cfelse>
23 <cfset dest = folder & "/" & getFileFromPath(newFile)>
24 </cfif>
25
26 <cffile action="move" source="#newFile#" destination="#dest#">
27
28 </cflock>
So a few notes on this code block. It's written for ColdFusion 9 which is why you use var keywords littered throughout. To get this to work in a CF8 CFC you would simply ensure you do the vars at the beginning.
The lock is critical here. In this case, all file moves are done via one FileService component. So the exclusive lock here ensures that only one move is done at a time. I did my best to minimize the size of the lock. It's only done after the upload processing and security checks. This is also an big hole in the logic. It assumes only ColdFusion has access to the folder. Obviously ColdFusion has no way to really lock a folder by itself. Some other process could drop a file in the folder at the same time.
I assume the main logic of this is pretty simple. I use a conditional loop that increments a counter which is prepended to the file name. In theory this should run pretty darn quickly. Outside of this block I've used logic to create date based folders so that ensures I don't end up with one folder that contains a few thousand files.
Lastly - there is another nice way to handle this as well. Use UUID file names. You can grab the extension of the original file and simply use the UUID as the filename. So foo.jpg becomes #createUUID()#.jpg. This will create an ugly, but unique file name, in far fewer lines of code. I'd use this if you don't care about the original file name. A good example of this would be for things like images.

Comment 1 written by Leigh on 6 November 2009, at 12:05 PM
-Leigh
Comment 2 written by Bobby Charles on 7 November 2009, at 9:11 AM
Comment 3 written by Raymond Camden on 7 November 2009, at 9:19 AM
Comment 4 written by jedifan on 8 November 2009, at 4:20 PM
What are you using for the lock name?
I would have assumed that to minimise the locking you would set the lock name to be name="#folder##filename#". That way it prevent collision with the same file, but allows multiple renames on different files at the same time.
Is that your value for lockname?
Cheers.
Comment 5 written by Raymond Camden on 8 November 2009, at 4:36 PM
Comment 6 written by Leigh on 8 November 2009, at 5:20 PM
Comment 7 written by Raymond Camden on 8 November 2009, at 5:23 PM
Comment 8 written by Leigh on 8 November 2009, at 5:39 PM
Comment 9 written by jedifan on 9 November 2009, at 7:26 AM
To get the granularity back into the locking, I would parse the filename to remove the special naming convention and again lock based on folder and stripped filename.
local.lockname = local.folder & ReReplace(local.filename, "^[0-9]*_", "");
Which would make the lock for "foo.cfm" and "1_foo.cfm" the same. Thereby reducing the scope of the lock to the safest minimum.
Comment 10 written by Adam Leszinski on 10 November 2009, at 1:33 AM
<!--- Known variables --->
<cfset ordNum = "00101"> <!--- example order num --->
<cfset ext = "jpg"> <!--- file extension --->
<cfset fileDir = "YOUR ABSOLUTE FILE PATH HERE">
<!--- use java's "java.io.File" to get a list of file names from the directory, no need to retrieve a query object from cfdirectory for just reading file names --->
<cfset dir = createObject("Java", "java.io.File").init(fileDir)>
<!--- get a comma delim list of file names --->
<cfset fileList = arrayToList(dir.list())>
<cfset count = listLen(fileList) - listLen(rereplacenocase("|"&replace(fileList,",","|,|","all")&"|","\|"&ordNum&"\."&ext&"\||\|"&ordNum&"\_[0-9]+\."&ext&"\|","","all"))>
<!--- default file name --->
<cfset newFileName = ordNum&"."&ext>
<cfif count>
<!--- if file name exists already, assign an incremented name, ie: fileName_#.ext --->
<cfset newFileName = ordNum&"_"&count&"."&ext>
</cfif>
<cfdump var="#newFileName#">
This approach avoids looping, looping over conditions, and fileExists() in exhange for a regex and a little math on the directory list. On the flip side there's really no one liner solution for cffile: action="rename" on naming conflicts.
[Add Comment] [Subscribe to Comments]