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 handle

FileSeek(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>