A reader asks:
I'm having trouble creating excel spreadsheets. can't get the line feeds to work right. it's all one blob.
Generating Excel files from ColdFusion, in general, is simple enough. Getting Excel to properly render the data, however, can be a real pain in the rear. Luckily, modern versions of Excel (Excel 2000 and higher) allow you to generate Excel files in simple HTML. This makes Excel reports about as simple as they can be. You can even include formulas and conditional formatting. Let's take a quick look at a simple example:
<cfset data = queryNew("name,age,gender")>
<cfloop index="x" from="1" to="50">
<cfset queryAddRow(data)>
<cfset querySetCell(data,"name","Name #x#")>
<cfset querySetCell(data,"age",randRange(20,40))>
<cfif randRange(0,1) is 1>
<cfset querySetCell(data,"gender","Male")>
<cfelse>
<cfset querySetCell(data,"gender","Female")>
</cfif>
</cfloop>
<cfcontent TYPE="application/msexcel">
<cfheader name="content-disposition" value="attachment;filename=report.xls">
<cfoutput>
<table width="100%" border="1">
<tr>
<th>Name</th><th>Gender</th><th>Age</th>
</tr>
</cfoutput>
<cfoutput query="data">
<tr
<cfif currentRow mod 2>bgcolor="##ffff80"</cfif>
>
<td>#name#</td>
<td>#gender#</td>
<td>
<cfif age gte 30>
<b>#age#</b>
<cfelse>
#age#
</cfif>
</td>
</tr>
</cfoutput>
<cfoutput>
<tr>
<td align="right" colspan="2"><b>Average:</b></td>
<td>=AVERAGE(c2:c#data.recordCount+1#)</td>
</tr>
</cfoutput>
<cfoutput></table></cfoutput>
I'll skip over the first few lines as all it is doing is creating my data. The important lines begin with the cfcontent and cfheader tags. These are the tags that tell the browser to expect Excel data from the response.
The rest of the code, in general, is nothing more than a simple HTML tag. I create a header. I loop over my query. I even do a bit of conditional formatting. You will notice that I change the bgcolor every other row, which makes things a bit easier to read. For the heck of it, I bolded the age of anyone over 30 (Logan's Run anyone?). The only real "special" code I included was this bit:
<td align="right" colspan="2"><b>Average:</b></td>
<td>=AVERAGE(c2:c#data.recordCount+1#)</td>
</tr>
The =AVERAGE line is a simple Excel formula that generates an average on the age of the people in my data. I knew that my age values were in column C, and that they started on the second line (the first line is the header). I then used data.recordCount+1 to ensure the average covered all the values from my data.
What is cool about this result is that, like any other Excel sheet, you can change the data and see the average update automatically.
Now - there is a lot more you can do with Excel then just averages, and do not forget that using this syntax requires a more modern version of Excel (although I think requiring a version from five years ago isn't so bad), but it is certainly a heck of a lot easier to generate the output.
One more tip. Sometimes waiting for Excel to launch, even on a zippy system, can be a pain. Especially if you are just trying to modify the formatting a bit. One nice thing about the "HTML option" for generating Excel is - you can simply comment out the cfcontent/cfheader tags and render your table on screen. Once you get it perfect there, you can return the lines back in and double check to make sure it is still good in Excel.
Edited: Readers of my blog made multiple mentions of the POI project. Dave Ross has two good URLs in the comments section of this entry!
Archived Comments
Ray,
Do you recommend this method to generate excel spreadsheets over using Jakarta POI?
Good Article.
Joel
I used to do the HTML --> Excel method, but now exclusively use POI. The main reason is file size. Using the HTML method, you are generating HTML (or XML) files that you "trick" Excel into opening. The problem here is the huge amount of markup around each data cell. You can end up with some very large files very quickly with this method.
Using POI, what you get are true binary Excel files, with a much more compact footprint. Plus, it's possible to do stuff in POI that is difficult or even impossible to do using the HTML method.
I've never used POI. I'll take a look at it. Frankly, I've only done "small" reports, so file size was never an issue, but I can see how it would be. I'll see if I can do some research and do a later blog entry on it.
Ray,
Feel free to take a look at the posts I've made about POI:
http://www.d-ross.org/index...
and
http://www.d-ross.org/index...
Buried in there is the true answer to this question ("can't get the line feeds to work right").
Cool stuff. No need for me to do the research and blog it - you already have. :)
well... I did the research, you can still blog it as an alternate answer (more people that know, the better... and not everyone scours comments).
Your worried, eh? Ok, I'll edit the entry and add a note to the bottom. :)
OpenXCF has had for a long time now a CFX tag that easily allows anyone to read and write XLS files using POI.
Forgive the OT remark (but it's my blog ;) - Holy smokes, Matt - where the heck have you been?
WOW, that's TWO of my questions you answered! you are the BOMB!
Ray, can you help me know how do we insert special characters in the excel sheet?
What problems are you having?
Hey Raymond, I have a query with various fields and I want to write it to a spreadsheet with some formatting.
In each column, I want to highlight the cells which has value lower than a specified value (conditional formatting for more than one column). Is there any way to accomplish this in CF10 without using a loop?
I don't believe so. I think you will need to add the conditional format to each column.
I searched a lot but wasn't sure about that. Thought I'd ask an expert.
Thanks. :)
To be clear, I'm not really an expert anymore - I don't do a lot of ColdFusion now.