I call this function from any one of my report handler functions using something like:

setNextEvent(event="report.makeXLS"persist="result"); 

rc.result is the results of what ever report query I am running. This allows me to simply create a XLS styled button on any of my reports and it dumps the results of the report to an excel spreadsheet.

Handler:

function makeXLS(event,rc,prc)
{
        rc.theSheet = SpreadsheetNew("Sheet1");
        LOCAL.metaData=getMetaData(rc.result);
        LOCAL.header="";
        for (LOCAL.i=1;LOCAL.i<arrayLen(LOCAL.metaData);LOCAL.i=LOCAL.i+1)
        {
        if(LOCAL.i eq 1) LOCAL.header=LOCAL.metaData[i].name;
        else LOCAL.header=LOCAL.header & ',' & LOCAL.metaData[i].name;
        }
   SpreadsheetAddRow(rc.theSheet,LOCAL.header,1,1,1);   
  SpreadsheetAddRows(rc.theSheet,rc.result,2,1,1);   
  event.setView("report/makeXLS");
}
 
A little note here. You may wonder why I build the header using the meta data. Well it's simple it's because if you use the coldfusion function is tries to do you a favor and alphabetically sorts the headers. This isn't what you want because then your headers and results won't match.
 
View:
<cfheader name="content-disposition" value="inline; filename=export.xls" />  
<cfheader name="content-transfer-encoding" value="binary" />  
<cfcontent type="application/msexcel" variable="#spreadsheetReadBinary(rc.theSheet)#" reset="true" />