Ask a Jedi: Working with Excel

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:

<cfsetting showdebugoutput=false>

<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:

<tr>
   <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

Comment 1 by Joel posted on 9/12/2005 at 7:37 PM

Ray,

Do you recommend this method to generate excel spreadsheets over using Jakarta POI?

Good Article.

Joel

Comment 2 by Rob Brooks-Bilson posted on 9/12/2005 at 7:59 PM

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.

Comment 3 by Raymond Camden posted on 9/12/2005 at 8:03 PM

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.

Comment 4 by dave ross posted on 9/12/2005 at 8:36 PM

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").

Comment 5 by Raymond Camden posted on 9/12/2005 at 8:38 PM

Cool stuff. No need for me to do the research and blog it - you already have. :)

Comment 6 by dave ross posted on 9/12/2005 at 8:50 PM

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).

Comment 7 by Raymond Camden posted on 9/12/2005 at 8:54 PM

Your worried, eh? Ok, I'll edit the entry and add a note to the bottom. :)

Comment 8 by Matt Liotta posted on 9/12/2005 at 9:41 PM

OpenXCF has had for a long time now a CFX tag that easily allows anyone to read and write XLS files using POI.

Comment 9 by Raymond Camden posted on 9/12/2005 at 9:46 PM

Forgive the OT remark (but it's my blog ;) - Holy smokes, Matt - where the heck have you been?

Comment 10 by michael White posted on 9/13/2005 at 8:36 PM

WOW, that's TWO of my questions you answered! you are the BOMB!

Comment 11 by Ramakrishna posted on 2/18/2009 at 3:58 PM

Ray, can you help me know how do we insert special characters in the excel sheet?

Comment 12 by Raymond Camden posted on 2/18/2009 at 5:09 PM

What problems are you having?

Comment 13 by Rahul Kumar posted on 2/16/2017 at 9:52 AM

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?

Comment 14 (In reply to #13) by Raymond Camden posted on 2/16/2017 at 12:29 PM

I don't believe so. I think you will need to add the conditional format to each column.

Comment 15 (In reply to #14) by Rahul Kumar posted on 2/16/2017 at 2:23 PM

I searched a lot but wasn't sure about that. Thought I'd ask an expert.
Thanks. :)

Comment 16 (In reply to #15) by Raymond Camden posted on 2/16/2017 at 2:53 PM

To be clear, I'm not really an expert anymore - I don't do a lot of ColdFusion now.