Skip to main content
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Sandy Clark
Ben Nadel at CFUNITED 2009 (Lansdowne, VA) with: Sandy Clark ( @sandraclarktw )

Relative File Paths Work In A ColdFusion File System

By on
Tags:

For years, I've been using regular expressions as a means to traverse file paths in my ColdFusion applications. By chopping-off the last directory "pattern" in a file path, I was able to move up into the parent directory:

<!--- Get the current directory. --->
<cfset currentDirectory = getDirectoryFromPath( getCurrentTemplatePath() ) />

<!---
	Get parent directory by chopping off the last directory of the
	current directory path: /(xxxxxxxx/)
--->
<cfset parentDirectory = reReplace(
	currentDirectory,
	"[^/]+/$",
	"",
	"one"
	) />

<!--- If the Hello file exists, execute it. --->
<cfif fileExists( parentDirectory & "relative_paths/subfolder/hello.cfm" )>

	<cfinclude template="./subfolder/hello.cfm" />

</cfif>

Here, you can see that I am using the reReplace() method to move up the currentDirectory path in order to obtain the parentDirectory path. Then, I am using this parentDirectory path in a fileExists() call. This code successfully runs and outputs the following message:

Hello from the SubFolder!

This works. But, it lacks clarity and readability. While the intent may be evident from my variable name choices, the implementation may remain a mystery to anyone who's not comfortable with regular expressions.

In the last few months, I've abandoned this convoluted approach to path traversal and replaced it with relative-path constructs in my ColdFusion path logic:

<!--- Get the current directory. --->
<cfset currentDirectory = getDirectoryFromPath( getCurrentTemplatePath() ) />

<!--- Get the parent directory using relative pathing. --->
<cfset parentDirectory = (currentDirectory & "../") />

<!--- If the Hello file exists, execute it. --->
<cfif fileExists( parentDirectory & "relative_paths/subfolder/hello.cfm" )>

	<cfinclude template="./subfolder/hello.cfm" />

</cfif>

Here, rather than chopping off the last directory, I am simply adding the relative-path construct, "../", to my currentDirectory value. And, when I run this code, I get the following output:

Hello from the SubFolder!

Not only does this work, it's significantly more intuitive than the regular-expression-based approach.

I used to dislike the idea of having "../" constructs in my ColdFusion file paths. But, this fear was emotional. For some reason, it felt dirty; like there was "clean up" left to be done on the file path and I wasn't doing it. But, I have since overcome this emotional limitation and started using the easier, more intuitive, more maintainable notation in my ColdFusion file paths.

A Note On Security

Using relative-file-path constructs can be dangerous if you're letting your uses define or manipulate paths. This is why (I believe) "../" constructs are disabled, by default, in ASP Classic applications. If you are using "../" file paths as part of your internal configuration, however, there is absolutely no security exposure.

Want to use code from this post? Check out the license.

Reader Comments

3 Comments

I've always found ColdFusion's lists (delimited with "/") to be an elegant way of traversing directories. (Though it doesn't fair as well with this example.)

<cfset parentDirectory = ListDeleteAt(currentDirectory, ListLen(currentDirectory, "/"), "/") />

That said, I might just like it because I have a few list utility routines to help me out here, and could write this as

<cfset parentDirectory = ListDrop(currentDirectory, 1, "/") />

(where ListDrop drops the last n elements of a list, n being it's second argument).

But at that point I might as well write my own set of directory traversal functions as well...

5 Comments

After I spent too much time with traversing file paths, supporting win (\) + lin (/) servers, I realized that actually I didn't want to traverse paths.

What I wanted is to hardcode filepaths in a platform independend way, so I came up with the idea of project relative paths. My project root is where the Eclipse .project file resides.

Instead of traversing the code looks like this:

if( fileExists( Path.getProject("/cache/myFile.ini") ) ){...}

As I code all that paths in a project fully qualified way, it is possible to use search & replace on refactorings.

The Path.cfc starts with:

relativeProjectPathOfThisFileRegEx = "project[/\\]path[/\\]of[/\\]this[/\\]Path.cfc";
project = reReplace(getCurrentTemplatePath(), relativeProjectPathOfThisFileRegEx, "");

and all path methods are build on top of that.

This is from the comments: The ColdFusion function expandPath() is usefull, but not reliable with virtual mappings, defined in IIS or Apache and also not reliable in environments with path rewriting (my experience, not documented). The functions getCurrentTemplatePath() and getBaseTemplatePath() are usefull for file operations relative to the current location or the webroot. Still, this class provides the easiest way, to cope with any system paths inside the project root.

15,663 Comments

@Thom,

I've also played with the list-based usage as well. The one thing that always bothered me was having to determine the length of the list - the listDrop() method would be nice for that (or rather, for not caring what the length is).

@Walter,

I think I used to use a similar approach where I used a Config.cfc instead of a Path.cfc; but then, inside the Path.cfc, I usually had some branch-logic for which server I was on (ex. Live vs Dev).

Now, I tend to just put all my path calculations inside my onApplicationStart() event handler. Luckily, I believe the path-support has gotten much stronger in the last few releases of ColdFusion (in terms of cross-OS path separators).

5 Comments

Just curious, do you set per application mappings or customtag paths in the Application.cfc, somehow based on that path calculations in the onApplicationStart? Sorry, a little off topic.

15,663 Comments

@Walter,

I tend to set up per-app mappings and tag paths (though less tag paths these days) in my Application.cfc pseudo constructor. Typically, I'll set up some sort of "root" directory property and then build off that:

<cfcomponent>
 
	<cfset this.rootDir = getDirectoryFromPath( getCurrentTemplatePath() ) />
 
	<cfset this.mappings[ "/com" ] = (this.rootDir & "com/") />
 
</cfcomponent>

... like that.

You can't set up per-app mappings in the onApplicationStart() as this is already *too late* for those mappings to take effect. They have to be calculated and defined in the pseudo-constructor.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel