Using Java objects to loop through a directory and check date last modified on files.
This is probably a no-brainer for some of you, but I was playing around with CFML's underlying Java capabilities the other day and thought I would share this little gem. Not that it's all that extraordinary, or even really useful, I've just got some free time and I haven't blogged anything in a couple of weeks so I figured "why not?".
The reason I even started playing with this in the first place was because of a blog post by Dave Ferguson a few days ago. He was using cfdirectory to get a list of files, then using a Query of Queries to get the files that had a modified date more than 2 days ago. Since I generally always try to use Java objects when working with directories and files in CFML, I wanted to see how easy it would be to figure out the file's last modified date with Java instead of the cfdirectory/QOQ approach. Not that there was anything really wrong with what Dave was doing, I just wanted to see if I could accomplish the same thing another way.
As it turns out, it was pretty doggone easy! Here's the code:
<cfset variables.myDir = "C:\inetpub\wwwroot" />
<!--- Set the date to compare against --->
<cfset variables.compDate = dateAdd("d",-2, now()) />
<!--- Create Java objects (one to check the files, one to check the date) --->
<cfset variables.objFile = CreateObject("java", "java.io.File") />
<cfset variables.objDate = CreateObject("java", "java.util.Date") />
<!--- Get a list of all directories and files in "myDir"; listFiles() actually returns an array. --->
<cfset variables.DirList = variables.objFile.init(variables.myDir).listFiles() />
<cfoutput>
<!--- Loop through the array --->
<cfloop from="1" to="#ArrayLen(variables.DirList)#" index="variables.i">
<!--- get our last modified date from the current item, and set it as a variable. --->
<cfset variables.curItemDate = variables.objDate.init(variables.DirList[variables.i].lastModified())>
<!--- do a date compare. --->
<cfif DateCompare(variables.curItemDate,variables.compDate,'d') eq -1>
<!--- Display File Path --->
#variables.DirList[variables.i]#<br />
</cfif>
</cfloop>
</cfoutput>
Now, the above example doesn't distinguish between files and directories, it checks the dates on both. Filtering out the directories is easy enough, though. If you only wanted to check the date last modified on files, just add in a cfif statement that checks the Java isFile() method.
<!--- Loop through the array --->
<cfloop from="1" to="#ArrayLen(variables.DirList)#" index="variables.i">
<!--- only check files; ignore directories. --->
<cfif variables.DirList[variables.i].isFile()>
<!--- get our last modified date from the current item, and set it as a variable. --->
<cfset variables.curItemDate = variables.objDate.init(variables.DirList[variables.i].lastModified())>
<!--- do a date compare. --->
<cfif DateCompare(variables.curItemDate,variables.compDate,'d') eq -1>
<!--- Display File Path --->
#variables.DirList[variables.i]#<br />
</cfif>
</cfif>
</cfloop>
</cfoutput>
As you can see, it's all pretty straight forward. The key piece of code I had to figure out was this line:
<cfset variables.curItemDate = variables.objDate.init(variables.DirList[variables.i].lastModified())>
The lastModified() method actually returns "A long value representing the time the file was last modified, measured in milliseconds since the epoch (00:00:00 GMT, January 1, 1970)"(quoted from the Java docs), which when output would look like something along the lines of "1266003559986". Now, having just gone through my little isDate() experiment, I knew that CFML does not play nicely when trying to format or use serial dates such as this. That's where the Java Date Class comes in to play. It takes the date returned by lastModified(), and converts it over to a good ole' date/time value that CFML can play with.
And that's about all it takes to loop through a list of files in a directory and get their last modified dates. Just to clarify, I'm not saying that this method of checking a file's last modified date is better than what Dave Ferguson was doing. I honestly have no idea if there is any type of benefit or performance boost by doing it this way. My reasoning for doing this wasn't to see if I could do it better, it was just to see if I could do it differently.


<!--- Set your directory --->
<cfset variables.myDir = "E:\inetpub\wwwroot" />
<!--- Set your time interval --->
<cfset variables.cutoffDays = 5>
<!--- Set the date to compare against --->
<cfset variables.compDate = dateAdd("d",-1 * variables.cutoffDays, now()) />
<cfoutput>
<h2>FILES in #variables.myDir# MODIFIED IN LAST #variables.cutoffDays# DAYS</h2>
<cfflush>
</cfoutput>
<cfscript>
traverseRecursive(variables.myDir);
</cfscript>
<cffunction name="traverseRecursive">
<cfargument name="thisDir">
<!--- Create Java objects (one to check the files, one to check the date) --->
<cfset var objFile = CreateObject("java", "java.io.File") />
<cfset var objDate = CreateObject("java", "java.util.Date") />
<!--- Get a list of all directories and files in "myDir"; listFiles() actually returns an array. --->
<cfset var dirList = objFile.init(arguments.thisDir).listFiles() />
<cfoutput>
<!--- Loop through the array --->
<cfloop from="1" to="#ArrayLen(dirList)#" index="LOCAL.i">
<!--- check files; recurse directories. --->
<cfif dirList[LOCAL.i].isDirectory()>
<cfscript>
traverseRecursive(dirList[LOCAL.i].toString());
</cfscript>
<cfelseif dirList[LOCAL.i].isFile()>
<!--- get our last modified date from the current item, and set it as a variable. --->
<cfset var curItemDate = objDate.init(dirList[LOCAL.i].lastModified())>
<!--- do a date compare. --->
<cfif DateCompare(variables.compDate, curItemDate, 'd') eq -1>
<!--- Display File Path --->
#dirList[LOCAL.i]#<br />
<cfflush>
</cfif>
</cfif>
</cfloop>
</cfoutput>
</cffunction>
<cfoutput>
<h3>DONE.</h3>
</cfoutput>