ColdFusion Muse

Ask-a-Muse: Working With File Paths in a Dual Environment

Mark Kruger April 7, 2009 10:33 AM ColdFusion Comments (4)

Muse Reader Asks:
To use cffile locally, I need to specify a filepath like c:\cfusionmx\wwwroot\uploaded_files for it to work. But if i upload it to the server, this filepath won't exist. Do i just put something like file="news.txt" or specify a URL?

This question is an excellent reminder to folks like me that there are many burgeoning ColdFusion developers out there that need some help with the fundamentals. I often write about complex tuning or obscure behaviors (with the server, not that thing I have about Jell-O and a duck). But sometimes us CF Bloggers need to go back and produce material for developers who are just starting out.

Your problem here, dear reader, is that you are working in a dual environment - testing code on your desktop and deploying it to the server. That's a very good thing. In fact, I applaud you for it since most folks start out the other way around, making changes to production server code and only finding out it is a bad idea right after they create their first infinite loop. In fact there are some nuances to a dual environment that you have to think about ahead of time. In general, an application of any size will need some "global variables" that are specific to its hosted environment. That's where the Application scope usually comes in - but we can save that post for a follow up. Right now let's tackle your specific issue.

There's a little function called "expandpath()" that is going to solve your dilemma quite nicely. Basically, it takes a snippet of "relative" path and uses it to create a physical path relative to where the script running it is located. That may sound daunting, but you are probably already doing something like this with images or css or JavaScript (as in src="../images/logo.jpg"). Here are some samples. In each sample I'm setting a variable called "fPath" to a physical location.

Current Directory

<!--- current path --->
<Cfset fPath = expandpath("./")/>
<!--- output --->
<cfoutput>#fPath#</cfoutput>


To use your example, if your code was in a file called "index.cfm" in a folder at "c:\cfusionmx\wwwroot\content", then the variable "fPath" would contain the string "c:\cfusionmx\wwwroot\content". So far so good, but there's more!

One Directory Back

In your example you have a directory called "uploaded_files" that is inside of the wwwroot folder. So what I really need is to find the directory that is "one up" from my "/content" directory. I need to move back up the chain. That looks like this.

<!--- current path --->
<Cfset fPath = expandpath("../")/>
<!--- output --->
<cfoutput>#fPath#</cfoutput>


The 2 dots and slash take you one directory back. It's just like working with the img tag. The code above would set fPath to "c:\cfusionmx\wwwroot" - and that's exactly what we were looking for. In fact, you can go back n number of directories using the same syntax.
<!--- current path --->
<Cfset fPath = expandpath("../../")/>
<!--- output --->
<cfoutput>#fPath#</cfoutput>


This would give you "c:\cfusionmx\".

Clean Up

Ok, so we have a directory that is the root (to take our second example). How do we use that with our CFFILE operation? It turns out that CFFILE takes a string as an argument for the "file" attribute (actually for all of it's arguments). So all we need to do is get our path looking right and we are done.

<cffile action="read"
        file="#fPath#/uploaded_files/news.txt"
        variable="news"/>

When this code runs on your local desktop it will pick up "c:\cfusionmx\wwwroot\" as the path, but when it runs on the server it will pick up whatever path the server admin has chosen for your web root - as in "d:\websites\blah.com\www" for example.

I hope this helps our reader. Keep those questions coming.

  • Share:

4 Comments

  • JC's Gravatar
    Posted By
    JC | 4/7/09 10:15 AM
    I've never used expandpath, actually. I always use GetCurrentTemplatePath() or GetBaseTemplatePath(). But any of them can be handy depending on what you're trying to do

    There are several useful functions like that here:

    http://livedocs.adobe.com/coldfusion/8/htmldocs/he...

    And you can use them in combination, like #GetFileFromPath(GetCurrentTemplatePath))# will get the filename of the CF file it's being called from; #GetDirectoryFromPath(GetCurrentTemplatePath))# will get the directory without the filename... #GetTempDirectory()# is useful

    Just keep in mind that the behaviours changed slightly in version 8 for at least GetCurrentTemplatePath() -- prior to 8 it was basically the same as GetBasetemplatePath() and they corrected it in 8 to point to the specific cf file where that code is located executed instead of the cf file being called by the browser

    And I'd be careful with expandpath, not to use it with a variable... seems like it might open you up to directory traversal attacks:
    expandpath(#foo#)
    ?foo=../../../../../../../../../etc/passwd
    or
    ?foo=../../../../../windows/system32/config/SAM or something...
  • mkruger's Gravatar
    Posted By
    mkruger | 4/7/09 10:24 AM
    JC,

    Excellent points. I like expand path because I don't have to parse down if I want to get something below where I am at. For example, I don't like storing files "on the root" of a web site. I usually have them one directory beneath the root. But your point is well taken. You would not want something like

    expandpath(url.pathtoexpand)

    roaming around in your code :)

    -Mark
  • JC's Gravatar
    Posted By
    JC | 4/7/09 12:14 PM
    Yeah.. honestly I usually just play it safe and use the same directory structure between all environments, or just use a CF mapping if it's something that's shared across domains. /com to D:\com\ or something.

    You could always use something like listdeleteat and listlen with the delimiter set to \, too.
  • Adam's Gravatar
    Posted By
    Adam | 1/22/10 3:37 PM
    Is expandPath not the correct function to use when try to output links to files?

    I am using this code:

    <cfset destination = expandPath("../../chancellorsProfessors/nominationUploads/#fname#_#lname#/")>


       <cfdirectory directory="#destination#" name="nomineeFiles">

       <cfoutput query="nomineeFiles">
          <ul>
             <li><a href="#destination##name#">#name#</a></li>
          </ul>
       </cfoutput>

    But the files are path files rather than hyperlinks. How else should I perform this action?