I have seen on some message boards people say to use cfinclude to read in files. I use cffile with action="read" to read in files right now. Is there a better way to do it? How can you read in a file using cfinclude?
NOTE: Charlie Arehart has pointed out serious flaws in my logic below, regarding GetPageContext().Include(). Please see my other blog post for more information.
Yes, you can read in files using the ColdFusion CFInclude tag rather than ColdFusion CFFile tag. However, I would not suggest doing it this way. CFFile was designed to read in files. It's nice and efficient. And, it can read in text files as well as binary files, something that the CFInclude tag cannot do.
When people read in files using CFInclude, they have to store the included data into a content buffer using the CFSaveContent tag:
<!--- Read file contents into strFileData variable. ---> <cfsavecontent variable="strFileData"> <!--- Read in the contents of the file. ---> <cfinclude template="./data.txt" /> </cfsavecontent> <!--- Trim data. ---> <cfset strFileData = Trim( strFileData ) />
When doing it this way, there are few issues. For starters, this it not very readable, at least not to me. Two, the CFInclude tag actually parses the included document. Most of the time you don't want to parse the document, as this can have HUGE processing overhead, you just want to read it in. And, finally, we have to trim the content since we are adding white space during the "reading" process. If the original data has leading or trailing white space, this will get lost.
To overcome the last two points above, we can use smarter white space control and the Include() method of the page context. The Include() method includes the file without parsing it:
<!--- Read file contents into strFileData variable. ---> <cfsavecontent variable="strFileData" <!--- Read in the contents of the file. ---> ><cfset GetPageContext().Include( "./data.txt" ) /></cfsavecontent>
Notice that we are not leaving any white space between our tags. This, to me, is still hard to read.
But, readability is one thing, let's test speed. I have created a rather large text document with 3.8 MILLION characters. I read this in using the CFFile tag as well as the CFSaveContent tag and outputted the character length:
<!--- Test the CFFile speed. ---> <cftimer label="CFFile" type="outline"> <!--- Read the file into strXmlData. ---> <cffile action="READ" file="#ExpandPath( './cffile_data.txt' )#" variable="strXmlData" /> <!--- Output the data character length. ---> File Length: <cfset WriteOutput( strXmlData.Length() ) /> </cftimer> <cftimer label="CFInclude" type="outline"> <!--- Read the file into strXmlData. ---> <cfsavecontent variable="strXmlData" <!--- Read in the file. ---> ><cfset GetPageContext().Include( "./cffile_data.txt" ) /></cfsavecontent> <!--- Output the data character length. ---> File Length: <cfset WriteOutput( strXmlData.Length() ) /> </cftimer>
The CFFile tag was faster than the CFSaveContent tag method on every test by at least 50 ms. Of course, keep in mind that this is a fairly large file (3.6 megabytes). Not only was CFFile faster, it was much more consistent. It always ran at about 63 ms - 78 ms. The CFSaveContent method, on the other hand, was very sporadic. Sometimes it was as low as 125 ms. Other times, it would spike to as much as 625 ms.
This makes a lot of sense to me. CFFile was built to read file data into a variable. If you go the CFSaveContent route, you are most likely taking more steps and creating additional buffers which means more processing which means more room for bottle necks. You are basically asking ColdFusion to work in a way that I would consider different than what it was supposed to do.
Ok, so if it's slower and inconsistent, why would people use CFSaveContent over CFFile? To me, the main reason is purely flexibility. When you use the CFSaveContent tag you can read in multiple files into one variable. Think about including sub-XML documents. You could create the parent XML node then include sub xml data files all into one CFSaveContent tag:
<!--- Read in site map. ---> <cfsavecontent variable="strXmlData"> <sections> <cfinclude template="./section_a.xml" /> <cfinclude template="./section_b.xml" /> <cfinclude template="./section_c.xml" /> <cfinclude template="./section_d.xml" /> </sections> </cfsavecontent>
This would incur all of the overhead we talked about before, but it can make combining multi-part documents very easy to do and maintain.
That being said, if you are just reading in a single file to use the data, please please please use the ColdFusion CFFile tag. Don't go getting all complicated for no reason.
Want to use code from this post? Check out the license.