Ask a Jedi: Dumping a Recursive Directory List

A reader (nicely) asked me something before I left for Boston, and I never got around to answering. He had an interesting problem. He wanted to list directories and files, in a recursive fashion, using HTML’s unordered list display to handle the directories and their children.

Now I thought this was a simple thing – just use the recurse=true option in <cfdirectory>. However – the more I thought about it – the more difficult it seemed. You can sort the <cfdirectory> result – but not in an way you can simply output with HTML.

My first thought was to switch back to a recursive <cfdirectory>, and while that would work, I assumed I’d lose a lot in terms of speed due to all the file operations. So what I came up with was a mix of recursive CFML and the built-in recursive <cfdirectory> tag:


<cfset initialDir = "c:\apache2\htdocs\testingzone\blogcfc_flex2">
<cfdirectory directory="#initialDir#" recurse="yes" name="files" sort="directory asc">

<cfset display(files,initialDir)>

<cffunction name="display" returnType="void" output="true">
<cfargument name="files" type="query" required="true">
<cfargument name="parent" type="string" required="true">
<cfset var justMyKids = "">

<cfquery name="justMyKids" dbtype="query">
select *
from arguments.files
where directory = <cfqueryparam cfsqltype="cf_sql_varchar" value="#arguments.parent#">
</cfquery>

<cfoutput><ul></cfoutput>

<cfoutput query="justMyKids">
<li>#directory#\#name#</li>
<cfif type is "Dir">
#display(arguments.files, directory & "\" & name)#
</cfif>
</cfoutput>

<cfoutput></ul></cfoutput>

</cffunction>

As you can see, I do the initial <cfdirectory> and have it fetch all the files. The UDF simply handles displaying items from the query. I don’t normally do output from UDFs, so to be honest, I feel a bit dirty. I’d probably just wrap it up in a cfsavecontent and return that, but this was written in about 5 minutes. Another problem – note I hard code \ as my file delimiter. I could have made this more dynamic by using a Java call:


<cfset separator = createObject("java","java.io.File").separator>

In general, the use of “/” will work just fine in any OS, however, since I was doing a string comparison in my query, I’d probably want to use the same separator CF used.