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:
Launch code in new window » Download code as text file »
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:
Launch code in new window » Download code as text file »
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:
Launch code in new window » Download code as text file »
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:
Launch code in new window » Download code as text file »
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.
Download Code Snippet ZIP File
Comments (6) | Post Comment | Ask Ben | Permalink | Other Searches | Print Page
Ben,
This is interesting. I had never thought to run performance tests. I'm not sure that I think the readability is a major issue.
I wonder what the performance difference is for very small files. I would think cfinclude and cfsavecontent would win in that case.
Keep in mind that some hosts have disabled cffile, making the cfinclude approach indispensible.
Posted by Steve Bryant on Oct 31, 2006 at 7:31 AM
Steve,
For small the files, there is no difference. I didn't notice any speed difference till I got above a couple HUNDRED THOUSAND lines of text. For small files or even relatively large files, I doubt there is going to be any noticeable difference between the two methodologies.
And, you raise a most excellent point; on a shared server where CFFile has been disabled, this is a most useful alternative. Plus, as I mentioned, it is quite nicely flexible. I use the CFSaveContent methodology to build both my Google Sitemaps and my URL redirection (404 handling).
As for readability, I am mildly retarded and I have trouble reading lots of stuff :)
Posted by Ben Nadel on Oct 31, 2006 at 7:36 AM
Steve,
Just one note on the small files and performance, they both performed at 0 ms. However, the CFSaveContent methodology would be slightly inconsistent and jump from 0 ms to 16 ms occationally where as CFFile was a consistent 0 ms. This is consistent with what I found for large files as well. I think there are more processing steps with CFSaveContent (intermediary buffers and what not) and this just leaves some more room for variability.
Posted by Ben Nadel on Oct 31, 2006 at 7:38 AM
Hi, Ben. Another couple of reasons to use this are when CFFILE is restricted (as it may be in the CF Admin), or even when the Java approach to reading a file is also restricted (as can be done when CFOBJECT/createobject are restricted).
Indeed, I may hold at least some responsibility for a recent increase in people mentioning this approach, as I offered it in a couple prominent places recently.
First, I offered it (along with the other ways to read a file) as my tips column in the first issue of the FusionAuthority Quarterly Update. Then I also offered this specific CFINCLUDE approach (again, for when the other approaches are restricted) as the answer/question for the CF Weekly Podcast guys to use in their "CFQuiz" segment a few weeks ago.
In all of them, I should add that I certainly wasn't proposing it as preferable over the more traditional approach (and certainly not for a large file). It was more just to say that it's there if one needed it.
Interesting comment about using getpagecontext include, though I should clarify that it does indeed parse the file just like a regular CFINCLUDE. Try it. The difference, though (and another tip), is that it processed the file like a full page request, in that it runs any application.cfm/cfc (unlike when a page is run via CFINCLUDE). Still, it could have some advantage over a plain include when reading a non-CFM file, so worth considering.
Posted by charlie arehart on Oct 31, 2006 at 7:43 AM
If it's not obvious, I was writing my note at the same time Steve was writing his, and posted it before reading his which made a similar point. :-)
Posted by charlie arehart on Oct 31, 2006 at 7:45 AM
Charlie,
No worries about showing people this method. Certainly, no one can argue with the benefits of having more tools in the old tool belt right?
I stand corrected about the whole GetPageContext().Include() points. I was under the impression that is did not parse the page. I was also not aware that it invoked the Application.cfm/cfc as well. This is good information to have. Thank you very much. I totally believe you, but I still have to test this as I like to see it in action ;)
Thanks for you and Steve filling in all the gaps that I did not point out (and corrections to my logic).
Posted by Ben Nadel on Oct 31, 2006 at 7:50 AM