Another new feature in ColdFusion 9 (and unfortunately not documented) is the new FileSeek ability. The basic idea of seeking in a file is jumping to an arbitrary position. This could be useful for a variety of reasons. For example, certain binary files may store information at the end of a file. Another example is getting the end of a long log file. I blogged about this back in April using Java via ColdFusion. ColdFusion 9 makes this somewhat easier with the addition of FileSeek.
As I said though, this is currently undocumented. Thanks to Rupesh for sending me the basics which I'll cut and paste right here:
FileOpen(path, mode, charset, seekable) - If seekable is true, you will be able to call fileSeek() and fileSkipBytes() . returns file handleFileSeek(fileObj, pos)
FileSkipBytes(fileObject, noOfBytesToSkip)
Seems easy enough, right? Here is an example that mimics the Java code from my previous example. First, define the file and create a file object for it:
<cfset theFile = "/Applications/ColdFusion9/logs/server.log">
<cfset fileOb = fileOpen(theFile, "read", "utf-8", true)>
Notice the new seekable argument there. Next, let's define a few variables:
<!--- number of lines --->
<cfset total = 10>
<cfset line = "">
Total is pretty obvious. The line variable will actually store my characters as I read it in. I should have called it buffer, or buffy, or maybe pinkpajamas.
<!--- go to the end of the file --->
<cfset pos = fileOb.size-1>
<cfset fileSeek(fileOb, pos)>
The next block of code uses the fileSeek. Notice that I define my position as the size of the file minus one. This will let me read a character in the code coming up.
<!--- go backwards until we get 10 chr(10) --->
<cfloop condition="listLen(line,chr(10)) lte total && pos gt 0">
<cfset c = fileRead(fileOb, 1)>
<cfset line &= c>
<cfset pos-->
<cfif pos gt 0>
<cfset fileSeek(fileOb, pos)>
</cfif>
</cfloop>
So this CFML code is pretty much the exact same as the Java-based code. Get a character. Add it to the line. Move backwards, and loop until we hit the beginning of the file or 10 lines. ColdFusion will do this for us, but it is a good idea to close the file:
<!--- close the file --->
<cfset fileClose(fileOb)>
Now we need to manipulate the string a bit. It is both reversed and has an additional character in it:
<!--- will always have one additional char --->
<cfset line = trim(mid(line, 1, len(line)-1))>
<!--- reverse it --->
<cfset line = reverse(line)>
And that's it! We now have a string with 10 lines from the end of the file. The complete template may be found below.
<cfset theFile = "/Applications/ColdFusion9/logs/server.log">
<cfset fileOb = fileOpen(theFile, "read", "utf-8", true)>
<!--- number of lines --->
<cfset total = 10>
<cfset line = "">
<!--- go to the end of the file --->
<cfset pos = fileOb.size-1>
<cfset fileSeek(fileOb, pos)>
<!--- go backwards until we get 10 chr(10) --->
<cfloop condition="listLen(line,chr(10)) lte total && pos gt 0">
<cfset c = fileRead(fileOb, 1)>
<cfset line &= c>
<cfset pos-->
<cfif pos gt 0>
<cfset fileSeek(fileOb, pos)>
</cfif>
</cfloop>
<!--- close the file --->
<cfset fileClose(fileOb)>
<!--- will always have one additional char --->
<cfset line = trim(mid(line, 1, len(line)-1))>
<!--- reverse it --->
<cfset line = reverse(line)>
<cfoutput>
<pre>
#line#
</pre>
</cfoutput>