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:
<cfset this.name = "myvfs">
<cfset variables.myroot = hash(getDirectoryFromPath(getCurrentTemplatePath()))>
<cfset variables.rootpath = "ram:///" & myroot & "/">
<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:
<cffunction name="onApplicationStart" access="public" returnType="boolean" output="false">
<!--- copy it so our code can use it --->
<cfset application.rootpath = variables.rootpath>
<cfif not directoryExists(application.rootpath)>
<cfdirectory action="create" directory="#application.rootpath#">
</cfif>
<cfreturn true>
</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:
<!--- testing simple file storage ... --->
<cfset logfile = application.rootpath & "log.txt">
<cffile action="append" file="#logfile#" output="Ran at #now()#">
<cfset contents = fileRead(logfile)>
<p/>
<form>
<textarea cols="50" rows="10"><cfoutput>#contents#</cfoutput></textarea>
</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:
<!--- testing for cfinclude ... --->
<cfset testfile = application.rootpath & "test.cfm">
<cfset code = "<cfset x = randRange(1,100)><cfoutput>##x##</cfoutput>">
<cffile action="write" file="#testfile#" output="#code#">
<h2>Testing the include...</h2>
<cfinclude template="/myvfs/test.cfm">
<cfset contents = fileRead(testfile)>
<p/>
<form>
<textarea cols="50" rows="10"><cfoutput>#contents#</cfoutput></textarea>
</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:
<cfdirectory directory="ram://" name="files" recurse="true">
<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:
<cffunction name="onApplicationEnd" access="public" returnType="void" output="false">
<cfargument name="appScope" type="any" required="true">
<cfdirectory action="delete" directory="#arguments.appScope.rootpath#" recurse="true">
</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.
Archived Comments
a VFS explorer would be a cool extension for CF Builder as well.
Yeah this has the potential to be a very nice feature, however I think it could have potential risks in a shared environment, I agree that it would be nice to be able to tie a virtual space to an application and likewise limit the amount of space available per app this would no doubt make it much more usable in shared environments.
It's interesting that something I took for granted on my Amiga would be considered cool some 22 years later! The ram drive was part of the OS and was especially useful for storing files you'd need to access quickly again and again, or even as a temporary place to hold images through several stages of processing.
haha Gary, that's because only Amiga makes it possible, only Amiga makes it happen! http://www.youtube.com/watc...
Robert, the first man in the video was my old boss, Anthony Jacobson! Wow, some old and wonderful memories. Now if only ColdFusion could handle 4096 colours and multitasking! ;-)
Been enjoying this feature in Railo for awhile now. Fun to use. :)
Since Railo is run on a per app basis it runs vfs per app which is a nice benefit. I am definitely starting to see the benefits of how Railo runs.
I would be curious if anyone could list a set of circumstances or criteria as to when it would be better to use VFS over the file system. I do understand the general concept of when you will be accessing a file repeatedly would be a good time to use it, but obviously images don't really apply in ColdFusion anymore. The only reason I ask is sometimes I find myself debating on items such as this feature as to when I want to actually perform the cross-over to using it.
@Daniel Budde:
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.
I believe I read somewhere that you could change
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 ?
@jody: Nope, not possible.
Ah, thanks for clearing that up.
Ray
We had similar app running in cf9 that created cfm files saved them to vfs and included them on the fly. Right after the include I just deleted the file. All worked great until we upgraded recently to cf11.
Now we got a strange error saying the global limit for vfs has been reached. Strange part is it happens on pages that are not related to the vfs system.
I have a forum post here for reference. https://forums.adobe.com/th...
I have a page that dumps.out the contents.of the vfs and when I get the error the total contents are no where near the global or application limit.
Have you or anyone seen any issues like this with cf11?
Thanks
Tim
I haven't played with the VFS in a long time. I read the thread, but it doesn't seem to help any. Sorry - best I can say is that I hope you get an answer, and I'd love to know more when/if you do.