ColdFusion Path Usage And Manipulation Overview

Posted March 26, 2007 at 8:40 AM

Tags: ColdFusion

Just a quick little ColdFusion path explanation. Sometimes I get confused about some path stuff, so I thought I would jot it down to help imprint in my grey matter. All path demonstrations use the following directory structure:

./index.cfm
./sub/sub.cfm

Additionally, we never call the sub.cfm page directly. Sub.cfm is included by index.cfm so that we can see how included sub-directories work with ColdFusion paths:

ExpandPath()

This creates an absolute path using a path relative to the directory of the currently executing page. This does NOT necessarily mean the currently executing template. If we run:

 Launch code in new window » Download code as text file »

  • #ExpandPath( "./" )#

... in both of the template, index.cfm and sub.cfm respectively give us:

D:\sites\kinky_solutions\site_v1\testing\paths\
D:\sites\kinky_solutions\site_v1\testing\paths\

Notice that regardless of the template from which it was called, ExpandPath() gives us an absolute path based on index.cfm, the currently executing page. This is one of the trickiest things for me when I deal with uploading files. I always have to remember that ExpandPath() is NOT relative to the action page I am processing - it is relative to my front controller.

GetCurrentTemplatePath()

This returns the absolute path of the currently executing template. The currently executing template is the one in which the code is being called from, not necessarily the path in the browser URL. If we run:

 Launch code in new window » Download code as text file »

  • #GetCurrentTemplatePath()#

... in both of the templates, index.cfm and sub.cfm respectively give us:

D:\sites\kinky_solutions\site_v1\testing\paths\index.cfm
D:\sites\kinky_solutions\site_v1\testing\paths\sub\sub.cfm

This is a really useful function when you need to get a path relative to the current template. To get the directory of this template, you can always use GetDirectoryFromPath(). If we run:

 Launch code in new window » Download code as text file »

  • #GetDirectoryFromPath(
  • GetCurrentTemplatePath()
  • )#

... in both templates, index.cfm and sub.cfm respectively give us:

D:\sites\kinky_solutions\site_v1\testing\paths\
D:\sites\kinky_solutions\site_v1\testing\paths\sub\

This combination of ColdFusion methods can help us get "ExpandPath" functionality bases on the currently executing template, NOT the currently executing page.

GetBaseTemplatePath()

This returns the absolute path the base template. This is the path of the template that maps to the currently executing page. If we run:

 Launch code in new window » Download code as text file »

  • #GetBaseTemplatePath()#

... in both templates, index.cfm and sub.cfm respectively give us:

D:\sites\kinky_solutions\site_v1\testing\paths\index.cfm
D:\sites\kinky_solutions\site_v1\testing\paths\index.cfm

As you case see, it gives us the absolute path of index.cfm (the currently executing page) regardless of which template it is called from. This is akin to running:

 Launch code in new window » Download code as text file »

  • #ExpandPath( CGI.script_name )#

... which will get an absolute path to the currently executing page.

GetDirectoryFromPath()

This takes a path of any kind and returns the a path that maps to the final directory in the given path. This is a utility function and does not rely on an existing path. If we run:

 Launch code in new window » Download code as text file »

  • #GetDirectoryFromPath( "/test/page.cfm" )#

... we get:

"/test/"

We just made up the path "/test/page.cfm" to prove that this has nothing to do with the physical file structure. This method will always leave a trailing slash and will not check to see if the current path is a directory. So, be caution, if you run:

 Launch code in new window » Download code as text file »

  • #GetDirectoryFromPath( "/I_AM_A_DIRECTORY" )#

... and do NOT include a trailing slash, it will return:

"/"

... because it doesn't care what is actually at the end of the given path if it is not a slash. So basically, this function removes every trailing character until it hits a slash or runs out of characters and then returns a path with a trailing slash.

GetFileFromPath()

This takes a path of any kind and returns the file at the end of the path. If we run:

 Launch code in new window » Download code as text file »

  • #GetFileFromPath( "/test/page.cfm" )#

... we get:

page.cfm

In this case, we used an obvious file name; however, this method really only returns everything it can starting at the end of the given path until it hits a slash. If we ran:

 Launch code in new window » Download code as text file »

  • #GetFileFromPath( "/test" )#

... where "test" is actually a directory, it will still return:

"test"

... since it is the most appropriate choice for a page (the path element located after the final path slash).

GetTempDirectory()

This gets the absolute path of the directory that ColdFusion uses for temporary files. This can be handy for uploading files:

 Launch code in new window » Download code as text file »

  • <cffile
  • action="UPLOAD"
  • filefield="file"
  • destination="#GetTempDirectory()#"
  • />

This will upload the given file into the temp directory.

GetTempFile()

Creates a temporary file (.tmp file) in a directory whose name starts with (at most) the first three characters of the given prefix. What took me a while to understand is that this actually created the physical file, it doesn't just create the file path. I suppose it does this so that no two calls to the same directory one after the other will return the same file path (that would be conflicting).

This can be nicely used in conjunction with GetTempDirectory():

 Launch code in new window » Download code as text file »

  • <cfset strPath = GetTempFile(
  • GetTempDirectory(),
  • "temp"
  • ) />

This will create a .TMP file in ColdFusion's temp file directory and try to use given the prefix. Now, the help docs say that this will use at most the first three characters of the prefix, but in my experience, this method usually uses the entire passed in prefix regardless of length.

Traversing A Path

There is nothing built into ColdFusion that makes traversing a file path very intuitive. In order to move up the directories in a path, I usually use one of three methods: List manipulation, a combination of path methods, or a regular expression.

If you look at a list, it's pretty much a path with the slash as the list item separator. Using this view, you can move up a directory by removing the last two items in the path - remember, the last item in the path is the file. This however can be awkward and if the last element is a slash (there is no file), list manipulation will not use the final empty element as a list item.

I find it best to use a combination of path methods to move up a directory:

 Launch code in new window » Download code as text file »

  • #GetDirectoryFromPath(
  • GetDirectoryFromPath(
  • GetCurrentTemplatePath()
  • ).ReplaceFirst( "[\\\/]{1}$", "" )
  • )#

This gets the current template path. Then, it gets the current directory of that path. Remember, GetDirectoryFromPath() always leaves us with a trailing slash. I then use a regular expression to delete that final slash. Then, I call GetDirectoryFromPath() on the resultant slash. This returns the path to the parent directory.

Then, there is the regular expression approach:

 Launch code in new window » Download code as text file »

  • #GetDirectoryFromPath(
  • GetCurrentTemplatePath()
  • ).ReplaceFirst(
  • "([^\\\/]+[\\\/]){1}$", ""
  • )#

This takes gets the directory of the current template path and then removes a single path item and trailing slash from the resultant path. If you want to move up more than one directory, you can replace the {1} with a {N} where N is the number of directories you want to move up in the path. This method is nice because it is short and can handle multiple directories in a single command. However, if you are super concerned with performance, running this regular expression will be slower than running the combo-option several times in a row. And, the more directories you want to move up in the path, the slower the regular expression becomes.

CGI Paths

The CGI object gives us several paths to work with.

CGI.cf_template_path

This gives us the absolute path of the currently executing page. This is the same as running:

 Launch code in new window » Download code as text file »

  • #ExpandPath( CGI.script_name )#

CGI.path_translated

This also returns the absolute path of the currently executing page. I am not sure what is the difference between this and cf_template_path.

CGI.script_name

This returns the web-page that is currently being executed.

So that's how ColdFusion paths work and a bit on how they can be manipulated. I hope that helps.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Other Searches  |  Print Page





Reader Comments

Mar 26, 2007 at 10:46 AM // reply »
92 Comments

Amazing there are no comments for this but maybe because its written so well. For those that have read this please bookmark it! Ben's work with programmatic configuration has done wonders for me. I've been able to have my applications figure out all my file paths so I don't have too and it was possible using these functions. They are very handy. Sorry I didn't notice this earlier Ben as I was on vacation. Bookmarked!


Mar 26, 2007 at 10:50 AM // reply »
7,538 Comments

@Javier,

Thanks a lot for the kind words :) No worries about the bookmarking - it was only posted a few hours ago ;) Glad all the programmatic config stuff is working out for you.


Mar 26, 2007 at 11:10 AM // reply »
92 Comments

Doh! I thought this was one of your older posts. You see I go through your blog daily and I thought when I opened this in a new tab that it was a post from a few days ago, not this morning. I didn't get a chance to read it right away. Still though what I said about bookmarking this still stands! :) Well done.


Mar 26, 2007 at 2:42 PM // reply »
15 Comments

Pretty nice Ben.

I already have a similar thing for myself written down in my "CF cookbook".


Mar 26, 2007 at 2:48 PM // reply »
7,538 Comments

Sounds tasty ;)


Jul 25, 2007 at 10:11 PM // reply »
2 Comments

Awesome regex for traversing up paths structures, thanks!


Dec 20, 2007 at 4:44 PM // reply »
1 Comments

I'm assuming this only works on Windows and not Linux or Unix?


Dec 20, 2007 at 4:48 PM // reply »
7,538 Comments

@Jim,

These are ColdFusion functions - they should be system independent (I think)? I know that there used to be some issues with the slashes, but I believe ColdFusion has done a better job of abstracting that in the most recent versions.


Jun 14, 2008 at 3:02 AM // reply »
6 Comments

correct these work on linux as well....

just make sure that you have permissions to write to the directory on the linux box....something you not even think twice about when using windows unless you have it locked down...


Oct 7, 2008 at 7:43 PM // reply »
2 Comments

Ben... Ben... dude... Thank you!

#GetDirectoryFromPath(
GetCurrentTemplatePath()
).ReplaceFirst(
"([^\\\/]+[\\\/]){1}$", ""
)#

The getDirectoryFromPath with the regex to delete.... Ohh Thank you!


Oct 8, 2008 at 8:32 AM // reply »
7,538 Comments

@John,

Glad you like :)


Mar 10, 2009 at 11:14 AM // reply »
7 Comments

Ben,
I'm working with a few application pages, and always want the root application page to be able to find configuration settings relative to its path when sent a reload param through the URL no matter where it's executed in the app. From what I can tell, all of the above functions use a path relative to the currently executing template. After digging around I've found the following Java that seems to reliably return the path of the application page it's on:

<cfset variables.thisPage = getPageContext().getFusionContext().getPagePath()>
<cfset variables.thisPagePath = getDirectoryFromPath(variables.thisPage)>

Is this the only way to get the path of a page, whether it's included somewhere or not? Is it as reliable as it seems?

I realize this is a rather old post, but a good one as always.


Mar 17, 2009 at 10:14 AM // reply »
7,538 Comments

@Ted,

Usually, when I need to always have a handle on a particular set of files, I put that code in the Application.cfc / cfm. This way, no matter what page gets calls, the Application file will be executed first and I can grab the files at known relative paths.

From within the Application.cfc, I use:

GetCurrentTemplatePath()

This always returns the full path of the Application.cfc file no matter what template the user requested.


Apr 27, 2009 at 8:46 PM // reply »
2 Comments

Thank you. This is really helpful.

There is a problem with this comment box, it keeps getting longer and longer! (Using Firefox 2.0)


Apr 29, 2009 at 8:03 AM // reply »
7,538 Comments

@Chuck1411,

Thanks. Yeah, someone else mentioned the comment box issue. I will try to debug it. I cannot replicate it with my Firefox, so it will be hard to debug.


Jun 16, 2009 at 5:21 AM // reply »
1 Comments

super super helpful thanks


Aug 11, 2009 at 1:36 PM // reply »
31 Comments

Very useful. Thanks.


Tom
Oct 9, 2009 at 10:37 AM // reply »
2 Comments

I hope this is not off topic, but one thing that I have not been able to get my arms around is the best practice for storing and accessing custom tags when you can't put them in the cf custom tag folder.

Ideally, I could have a custom tag folder in my application that would work the same way as the cf custom tag folder (automatic recursive searchers in sub directories). But even if I can't I would like to have a custom_tag that I could refer to no matter what template was running.

Is there a way using what you have provided above for doing this? Using cfmodule or cfimport to access them in a common manner (without having to consider the location of the currently running page).

Thanks in advance for your thoughts.

Tom


Oct 9, 2009 at 3:45 PM // reply »
7,538 Comments

@Tom,

Custom tags paths are a bit different from the stuff I have outlined. What version of ColdFusion are you running? If you are running CF8, you can set up a per-application custom tag pathing.

If not, you can always use CFImport to import the custom tag paths... or CFModule to provide a template path to the given custom tag.


Tom
Oct 12, 2009 at 10:57 AM // reply »
2 Comments

Ben - Thanks for you response. I am using CF8 and have setup an application specific mapping to the root of my app. Thank you for the suggestion.

So now I can use cfmodule and call the custom tag with an absolute path "/app/custom_tags/my_custom_tag.cfm". It seems to be working like a charm.


Oct 15, 2009 at 2:06 PM // reply »
7,538 Comments

@Tom,

Awesome dude!! Yeah, the app-specific mappings introduced in ColdFusion 8 are fantastic!


Feb 23, 2010 at 2:35 AM // reply »
18 Comments

Anyone ever see this: expandpath("/") returns:

E:\ColdFusion9\wwwroot\

instead of

D:\inetpub\wwwroot

(your drive letters will vary)

I have an application that was working well (front controller, etc., etc.) until an OS hard drive crashed on me, one of a two disk RAID 0 array. @#$%#@! cheap SSD hard drives! Only 4 months on these stupid Patriot PS-100 crap drives. Anyway, I digress...

After the rebuild, expandpath("/") is returning this craziness!!!!! Apparently, I installed CF without pointing at the right web root? I can't remember. CFIDE and docs and other stuff work right...

In my case, the app needs physical paths to things in other directory structures off the web root, and the app has to be agnostic about where it lives (could be in a dir off webroot, could be three dirs deep off webroot. It will vary on where it is deployed). So, I need all the parts:

If path to app is:
d:/inetpub/wwwroot/clientname/clientapp/version3/application.cfc

I need these vars:

webroot=d:/inetpub/wwwroot
siteroot= /clientname/clientapp*
appdir= /version3

* When deployed, siteroot may be null if appdir in webroot (with version3 becoming appname).

I had been using this to get this stuff (application.cfc)

(Thanks to Ben and his handy .ReplaceFirst() trick)

// chop off "\application.cfc"
application.appdir = GetCurrentTemplatePath().ReplaceFirst( "[\\\/][^\\\/]*$", "" );

// strip off the webroot, what is left is the path off webroot to the app
application.appdir = replacenocase(application.appdir, expandpath("/"), "");

// rationalize stupid windows legacy slashes
application.appdir = replace(application.appdir, "\", "/", "all");

// get the last dir (for some url rewrite magic processing in the front controller)
application.appsignifier = "/" & listlast(application.appdir, "/");

// the rest is the path off webroot to the directory that contains the app
application.siteroot = "/" & listdeleteat(application.appdir, listlen(application.appdir, "/"), "/");

After the rebuild, this all stopped working because of the wanky result of expandpath("/"). Although I never figured out where my install of CF went wrong, the fix was to explicitly set the webroot in Coldfusion. To do this I edited <cfinstalldir>/wwwroot/WEB-INF/jrun-web.xml and added this:

<virtual-mapping>

<resource-path>/*</resource-path>

<system-path>D:/wwwroot</system-path>

</virtual-mapping>

See: http://www.adobe.com/support/coldfusion/adv_development/config_builtin_webserver/config_builtin_webserver04.html

Hope this helps somebody sometime in the future. If anyone has any tricks for getting webroot without using expandpath("/"), please post. Thanks.

Sean


Feb 23, 2010 at 10:53 AM // reply »
31 Comments

Another thing you can do with file paths, especially if you're on a shared server or your files move drives regularly, is to save the primary path in a database. Query it when you need it. That way when you need to change it, you only have to change ONE place.


Feb 23, 2010 at 9:00 PM // reply »
7,538 Comments

@Sean,

Is there any CF admin mapping of per-application mapping that is, for some reason, pointing the "/" to the wrong directory?

I typically use the approach you outlined in the latter half of the post - using the path returned within the Application.cfc; then, I use that base path to build the other paths.

@Brian,

That's an interesting idea. I haven't worked on a lot of shared hosting (mostly VPS); but, I could see that being good.


Feb 24, 2010 at 12:38 AM // reply »
18 Comments

@Ben,

Nope. Fresh CF install, no mappings. Strangest thing I've seen in 5+ years using CF. To lazy to reinstall to see if that fixed it.

@Brian,

Unfortunately, no central db. If you care, more background below.

Sean

The app manages multiple instances. Each instance has it's own db, or multiple instances at one client may share a db. In any case, the app discovers from a file in each instance directory the DSN. No central config to manage. The app adds the dsn to a memory map the first time an instance url is encountered. What is an instance url? Read on.

App URLs are of the format:
webroot/sitepath/instpath/appsignifier/controllerpath/controller/param1><paramval1...

ex: thedomain.com/meat/shop/membersonly/view/specials

The "shop" appsignifier splits the url into two parts: instance path and controller path, and triggers a URL rewrite to become:

/index.cfm?i=/meat&q=/membersonly/view/specials

At a hosting company, expandpath() might return:

d:\inetpub\wwwroot\cust1232\thedomain\index.cfm

url params are parsed to id=specials and added to url scope. Instance info loaded from the db becomes a instance structure stuffed into the request.context structure.

The controller can choose to walk back up the controller path to read membersonly as a filter...or not.

&q is walked for a controller file (only once to build a memory map) and view.cfm is found at /core, but could be overridden if view.cfm is found in either /meat/ or /meat/membersonly.

So that's what all the walking is about...building the database link and controller memory map for each distinct url path.


Post Comment  |  Ask Ben

Recent Blog Comments
Mar 17, 2010 at 11:36 PM
Learning ColdFusion 8: CFImage Part I - Reading And Writing Images
@Ben, It is. But I have tested again and sleep() seem to be keeping windows hanging. As a result, multiple people were having issues uploading and removing images when they are trying to do the imag ... read »
Mar 17, 2010 at 11:22 PM
Google Maps Not Working in Internet Explorer (IE)
@James The page has a problem in IE7, because line 112 has a comma at the end. Line 112 is the first problem, there are many more with the same. Ralph ... read »
Mar 17, 2010 at 8:39 PM
Looping Over ColdFusion JSON Queries In jQuery
Thanks Ben - this is just what I needed - just getting up to speed with jQuery - and your posts are accelerating that process substantially :) ... read »
Mar 17, 2010 at 7:50 PM
ColdFusion ArraySplice() Method As An Example Of A Dynamic Method Signature
Java is awesome and I love seeing new twists on it. @Ben - I totally agree with you. ... read »
Mar 17, 2010 at 4:13 PM
Testing For NULL Values In A ColdFusion Query Result Set
To get around the empty strings in an UPDATE statement I pretended the variable was a string by enclosing it in single quotes, CAST it to an integer, let it convert empty strings to zeroes, and used ... read »
Mar 17, 2010 at 4:12 PM
Ask Ben: Environment-Based Application.cfc Settings
Ben, those are valid comments. I posted some code that shows how to "worryfree ;o)" copy code and let the Application.cfc sort it out. http://boncode.blogspot.com/2010/03/cf-dynamically-changing-ap ... read »
Mar 17, 2010 at 3:32 PM
Using Appropriate Status Codes With Each API Response
@Marc, For our project it isn't really a requirement, since most (if not all) of our resources are private and related to the logged in user. Anyway, IIRC google does execute javascript before inde ... read »
Mar 17, 2010 at 3:24 PM
Finally Finished Ayn Rand's Atlas Shrugged Audio Book
Objectivism is a form of positivism, and Quantum Mechanics does not fit positivism. One of the postulates of Quantum Mechanics is the impossibility of separating the object of measurement from the me ... read »