Posted in ColdFusion | Posted on 08-02-2009 | 3,559 views
One of the cooler new features of ColdFusion 9 is the Virtual File System (VFS). I've blogged on it before and also authored a quick video on it as well. One thing that I kind of don't like though is the fact that the VFS is system wide, not application specific. I guess that kind of makes sense - the file system is system wide as well, but with the VFS being RAM based, it seems like it would have been cooler if it was tied to an application, not the entire server.
For example, let's say I want to use the VFS in BlogCFC 7 (coming soon, I promise!). Since BlogCFC can be installed any number of times on one machine, I wouldn't be able to use a static VFS root like ram:///blogcfc. If I did, one BlogCFC install would overwrite the data of another BlogCFC install. I thought I'd whip up some simple code demonstrating one way to get around this.
Let me begin with the Application.cfc file for our sample application. First up - the constructor area:
2
3<cfset variables.myroot = hash(getDirectoryFromPath(getCurrentTemplatePath()))>
4<cfset variables.rootpath = "ram:///" & myroot & "/">
5<cfset this.mappings["/myvfs"] = variables..rootpath>
The application name (myvfs) isn't really important here - it can be set to anything. The myroot value though is critical. I needed a unique value for my VFS storage area. I get this by creating a hash from the current template path. I use code like this in BlogCFC when naming the application. The end result is a hashed string based on the file path. This hash will be unique across the server. From that hash, I create a variable, rootpath, that points to the VFS. Lastly I create a mapping as well. (The reason why will become apparent a bit later.)
Now let's take a look at the onApplicationStart method:
2 <!--- copy it so our code can use it --->
3 <cfset application.rootpath = variables.rootpath>
4 <cfif not directoryExists(application.rootpath)>
5 <cfdirectory action="create" directory="#application.rootpath#">
6 </cfif>
7
8 <cfreturn true>
9</cffunction>
First note that I create an application scoped copy of the rootpath value. This will let my application files easily use the same root directory. If the directory does not exist (it shouldn't!) then I create it. By the way - wondering why I didn't make a script based CFC? A reader commented on one of my other posts that the script based CFCs were a bit confusing. I'm trying to simplify my ColdFusion 9 posts so you will see more tag based CFCs. I won't always use them - I really do prefer script based CFCs, but I'd like to ensure folks can focus on learning one thing at a time. It's how I learn best as well. Also, one of the areas where cfscript fails is in directory operations. You can check for the existence of a directory, but you can't add, edit, or delete directories. I've logged a ER for this and I'm sure it will be fixed for final.
Ok, now for the first test. Here is the index.cfm file I created:
2<cfset logfile = application.rootpath & "log.txt">
3<cffile action="append" file="#logfile#" output="Ran at #now()#">
4
5<cfset contents = fileRead(logfile)>
6<p/>
7<form>
8<textarea cols="50" rows="10"><cfoutput>#contents#</cfoutput></textarea>
9</form>
I create a pathname based on the application.rootpath values. I then simply append to it. I load it up in a textarea so I can also see the results. After a few runs, I see this:

So that demonstrates simple file reading and writing, but what about an example using cfinclude? As you know, cfinclude can't use a real path. It has to use a relative path or mapping. Remember when we created the mapping? Now we can make use of it:
2<cfset testfile = application.rootpath & "test.cfm">
3<cfset code = "<cfset x = randRange(1,100)><cfoutput>##x##</cfoutput>">
4
5<cffile action="write" file="#testfile#" output="#code#">
6
7<h2>Testing the include...</h2>
8<cfinclude template="/myvfs/test.cfm">
9
10<cfset contents = fileRead(testfile)>
11<p/>
12<form>
13<textarea cols="50" rows="10"><cfoutput>#contents#</cfoutput></textarea>
14</form>
In this example, my virtual file is named test.cfm. It contains CFML code to generate and output a random number from 1 to a 100. The cfinclude tag includes it and executes the CFML I generated. I kept the textarea in so you can see the code saved to the VFS:

Woot! Works like a charm, right? I built a super simple VFS dumper and put it in the root of my CF9 install. The code just uses cfdirectory and a dump:
2
3<cfdump var="#files#" hide="attributes,mode">
I verified that my demo site was storing it's files within it's own folder:

Ok, so now for the final step. Since the VFS is not application based, what's going to happen when my application times out? Nothing! It would be nice if the application cleaned up after itself, right? Check out this simple solution:
2 <cfargument name="appScope" type="any" required="true">
3 <cfdirectory action="delete" directory="#arguments.appScope.rootpath#" recurse="true">
4</cffunction>
Since the cfdirectory tag allows for recursive deletes, I just need one tag to clean up the entire folder. So my code could make use of multiple files and folders under the rootpath values and they will all be cleaned up with the application times out.
I've included a zip of the demo folder. Let me know how it works out. By the way - if I get time, I'm going to whip up a quick CF Admin Extension that includes a simple VFS 'explorer' to let you browse, view, manage the VFS from your Administrator.


It would be faster to create thumbnails of images in VFS and move them into place than it would be to do the same on a file system. So, anything that has intense disk operations could be done faster via VFS. Writing to a log file, etc.
As a side effect: You could use CF to create / pull code from a db, write a CFC/CFM in VFS and invoke it in VFS. Something you could never do.
You could secure your uploads, making sure that the file that was uploaded was legit before moving it onto the File System. There are, of course, issues with that. If someone tries to upload a 120 Gig zipfile, obviously VFS is going to barf.
So, of course, like everything else you code, there's a time and a place and it's up to you to be responsible.
the location eg.) ram:/// to something else like
session:///. Now if that was true couldn't you just
create a temporary location for each user, or is that not pisslbe ?
[Add Comment] [Subscribe to Comments]