Creating Microsoft Excel Documents With ColdFusion And XML

Posted January 8, 2007 at 8:13 AM

Tags: ColdFusion

A little while back I posted about how to create simple Microsoft Excel documents using HTML. That's good and all and provides some basic formatting, but it is a definite hack. Someone suggested to me that I look into using XML to create Excel documents. As it turns out, using XML to create an Excel file is not only easy, it provides much more control over formatting, not to mention that I can rock cell formulas and a bunch of other things that make Excel great. But, perhaps the coolest thing is that, unlike the HTML Table method, the XML document opens up in Microsoft Excel document as if it were a true Excel document; the entire grid is laid out nicely, tabs are there, it's beautiful.

To start off with, I am going to show you how to create a simple data grid based on a query. As always, let's build a query to test with. For this one, it will be a query of movies, their name, rating, and some additional information:

 Launch code in new window » Download code as text file »

  • <!--- Create query to ouptut to excel. --->
  • <cfset qMovie = QueryNew(
  • "id, name, date_watched, rating, has_fighting, has_boobies",
  • "CF_SQL_INTEGER, CF_SQL_VARCHAR, CF_SQL_DATE, CF_SQL_DECIMAL, CF_SQL_BIT, CF_SQL_BIT"
  • ) />
  •  
  • <!--- Add rows to query. --->
  • <cfset QueryAddRow( qMovie, 5 ) />
  •  
  • <!--- Set row data. --->
  • <cfset qMovie[ "id" ][ 1 ] = JavaCast( "int", 1 ) />
  • <cfset qMovie[ "name" ][ 1 ] = JavaCast( "string", "Terminator 2" ) />
  • <cfset qMovie[ "date_watched" ][ 1 ] = JavaCast( "float", "2006/05/25" ) />
  • <cfset qMovie[ "rating" ][ 1 ] = JavaCast( "float", 10.0 ) />
  • <cfset qMovie[ "has_fighting" ][ 1 ] = JavaCast( "boolean", true ) />
  • <cfset qMovie[ "has_boobies" ][ 1 ] = JavaCast( "boolean", false ) />
  •  
  • <cfset qMovie[ "id" ][ 2 ] = JavaCast( "int", 2 ) />
  • <cfset qMovie[ "name" ][ 2 ] = JavaCast( "string", "American Pie" ) />
  • <cfset qMovie[ "date_watched" ][ 2 ] = JavaCast( "float", "2005/08/02" ) />
  • <cfset qMovie[ "rating" ][ 2 ] = JavaCast( "float", 9.0 ) />
  • <cfset qMovie[ "has_fighting" ][ 2 ] = JavaCast( "boolean", false ) />
  • <cfset qMovie[ "has_boobies" ][ 2 ] = JavaCast( "boolean", true ) />
  •  
  • <cfset qMovie[ "id" ][ 3 ] = JavaCast( "int", 3 ) />
  • <cfset qMovie[ "name" ][ 3 ] = JavaCast( "string", "Friends With Money" ) />
  • <cfset qMovie[ "date_watched" ][ 3 ] = JavaCast( "float", "2006/06/21" ) />
  • <cfset qMovie[ "rating" ][ 3 ] = JavaCast( "float", 8.0 ) />
  • <cfset qMovie[ "has_fighting" ][ 3 ] = JavaCast( "boolean", false ) />
  • <cfset qMovie[ "has_boobies" ][ 3 ] = JavaCast( "boolean", false ) />
  •  
  • <cfset qMovie[ "id" ][ 4 ] = JavaCast( "int", 4 ) />
  • <cfset qMovie[ "name" ][ 4 ] = JavaCast( "string", "Better Than Chocolate" ) />
  • <cfset qMovie[ "date_watched" ][ 4 ] = JavaCast( "float", "2006/10/07" ) />
  • <cfset qMovie[ "rating" ][ 4 ] = JavaCast( "float", 8.5 ) />
  • <cfset qMovie[ "has_fighting" ][ 4 ] = JavaCast( "boolean", true ) />
  • <cfset qMovie[ "has_boobies" ][ 4 ] = JavaCast( "boolean", true ) />
  •  
  • <cfset qMovie[ "id" ][ 5 ] = JavaCast( "int", 5 ) />
  • <cfset qMovie[ "name" ][ 5 ] = JavaCast( "string", "Real Genius" ) />
  • <cfset qMovie[ "date_watched" ][ 5 ] = JavaCast( "float", "2006/12/12" ) />
  • <cfset qMovie[ "rating" ][ 5 ] = JavaCast( "float", 9.0 ) />
  • <cfset qMovie[ "has_fighting" ][ 5 ] = JavaCast( "boolean", false ) />
  • <cfset qMovie[ "has_boobies" ][ 5 ] = JavaCast( "boolean", false ) />

Now that we have a our query, let's create a Microsoft XML Excel document with a header row followed by data rows:

 Launch code in new window » Download code as text file »

  • <!---
  • Store the XML Excel data. We are storing it first so that
  • we can clean up the data afterwards and then stream it as
  • a binary object to the browser.
  • --->
  • <cfsavecontent variable="strXmlData">
  • <cfoutput>
  •  
  • <!---
  • Define this document as both an XML doucment and a
  • Microsoft Excel document.
  • --->
  • <?xml version="1.0"?>
  • <?mso-application progid="Excel.Sheet"?>
  •  
  • <!---
  • This is the Workbook root element. This element
  • stores characteristics and properties of the
  • workbook, such as the namespaces used in
  • SpreadsheetML.
  • --->
  • <Workbook
  • xmlns="urn:schemas-microsoft-com:office:spreadsheet"
  • xmlns:o="urn:schemas-microsoft-com:office:office"
  • xmlns:x="urn:schemas-microsoft-com:office:excel"
  • xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet"
  • xmlns:html="http://www.w3.org/TR/REC-html40">
  •  
  • <!---
  • The first child element of the WorkBook element
  • is DocumentProperties. Office documents store
  • metadata related to the document—for example,
  • the author name, company, creation date, and
  • more in the DocumentProperties element.
  • --->
  • <DocumentProperties
  • xmlns="urn:schemas-microsoft-com:office:office">
  • <Author>Ben Nadel</Author>
  • <Company>Kinky Solutions</Company>
  • </DocumentProperties>
  •  
  • <!---
  • The Styles node represents information related
  • to individual styles that can be used to format
  • components of the workbook.
  • --->
  • <Styles>
  •  
  • <!--- Basic format used by all cells. --->
  • <Style ss:ID="Default" ss:Name="Normal">
  • <Alignment ss:Vertical="Top"/>
  • <Borders/>
  • <Font/>
  • <Interior/>
  • <NumberFormat/>
  • <Protection/>
  • </Style>
  •  
  • <!---
  • This is the movie rating style. We are going to
  • format the number so that it goes to one
  • decimal place.
  • --->
  • <Style ss:ID="Rating">
  • <NumberFormat ss:Format="0.0" />
  • </Style>
  •  
  • <!---
  • This is the date of the movie viewing. It is
  • going to be a short date in the format of
  • d-mmm-yyyy (ex. 15-Mar-2007).
  • --->
  • <Style ss:ID="ShortDate">
  • <NumberFormat ss:Format="[ENG][$-409]d\-mmm\-yyyy;@" />
  • </Style>
  •  
  • <!---
  • This is the boolean format of the has
  • fighting and has boobies columns. We are
  • going to display these values in Yes /
  • No format.
  • --->
  • <Style ss:ID="YesNo">
  • <NumberFormat ss:Format="Yes/No" />
  • </Style>
  •  
  • </Styles>
  •  
  •  
  • <!---
  • This defines the first worksheeet and it's name.
  • We are only using one worksheet in this example,
  • but you could add more Worksheet nodes after
  • this one for multiple tabs. The "Name" attribute
  • here is the name that shows up in the tab.
  • --->
  • <Worksheet ss:Name="Movie Data">
  •  
  • <Table
  • <!---
  • We need a column for each column of the
  • query. This attribute is required to be
  • correct. If the value here does NOT
  • match the data in Excel file, the
  • document will not render properly.
  • --->
  • ss:ExpandedColumnCount="#ListLen( qMovie.ColumnList )#"
  •  
  • <!---
  • We need a row for every query record
  • plus one for the header row. Again, if
  • this value does not match what is in the
  • document, the excel file will not
  • render properly.
  • --->
  • ss:ExpandedRowCount="#(qMovie.RecordCount + 1)#"
  •  
  • x:FullColumns="1"
  • x:FullRows="1">
  •  
  • <!---
  • Here, we can define general properties
  • regarding each column in the data output.
  • --->
  • <Column ss:Index="1" ss:Width="30" />
  • <Column ss:Index="2" ss:Width="100" />
  • <Column ss:Index="3" ss:Width="42" />
  • <Column ss:Index="4" ss:Width="84" />
  • <Column ss:Index="5" ss:Width="66" />
  • <Column ss:Index="6" ss:Width="70" />
  •  
  • <!---
  • This is our header row. All cells in the
  • header row will be of type string.
  • --->
  • <Row>
  • <Cell>
  • <Data ss:Type="String">ID</Data>
  • </Cell>
  • <Cell>
  • <Data ss:Type="String">Name</Data>
  • </Cell>
  • <Cell>
  • <Data ss:Type="String">Rating</Data>
  • </Cell>
  • <Cell>
  • <Data ss:Type="String">Date Watched</Data>
  • </Cell>
  • <Cell>
  • <Data ss:Type="String">Has Fighting</Data>
  • </Cell>
  • <Cell>
  • <Data ss:Type="String">Has Boobies</Data>
  • </Cell>
  • </Row>
  •  
  • <!--- Loop over the query. --->
  • <cfloop query="qMovie">
  •  
  • <!---
  • When we output the excel XML row / cell
  • data, we can put the format the tabbing
  • / returning of the Cell and data cells
  • in relation to each other; however, we
  • cannot freely move around the values
  • within the Data cells as it may change
  • the ability to convert the data type.
  • For instance, we cannot put any white
  • space in front of a numeric value or it
  • will not be parsed as a number and will
  • error out.
  • --->
  • <Row>
  • <Cell>
  • <Data ss:Type="Number">#qMovie.id#</Data>
  • </Cell>
  • <Cell>
  • <Data ss:Type="String">#qMovie.name#</Data>
  • </Cell>
  • <Cell ss:StyleID="Rating">
  • <Data ss:Type="Number">#qMovie.rating#</Data>
  • </Cell>
  • <Cell ss:StyleID="ShortDate">
  • <Data ss:Type="DateTime">#DateFormat( qMovie.date_watched, "yyyy-mm-dd" )#T#TimeFormat( qMovie.date_watched, "HH:mm:ss.l" )#</Data>
  • </Cell>
  • <Cell ss:StyleID="YesNo">
  • <Data ss:Type="Number">#qMovie.has_fighting#</Data>
  • </Cell>
  • <Cell ss:StyleID="YesNo">
  • <Data ss:Type="Number">#qMovie.has_boobies#</Data>
  • </Cell>
  • </Row>
  •  
  • </cfloop>
  •  
  • </Table>
  •  
  • </Worksheet>
  •  
  • </Workbook>
  •  
  • </cfoutput>
  • </cfsavecontent>
  •  
  •  
  • <!---
  • Define the way in which the browser should interpret
  • the content that we are about to stream.
  • --->
  • <cfheader
  • name="content-disposition"
  • value="attachment; filename=basic.xml"
  • />
  •  
  • <!---
  • When streaming the Excel XML data, trim the data and
  • replace all the inter-tag white space. No need to stream
  • any more content than we have to.
  • --->
  • <cfcontent
  • type="application/msexcel"
  • variable="#ToBinary( ToBase64( strXmlData.Trim().ReplaceAll( '>\s+', '>' ).ReplaceAll( '\s+<', '<' ) ) )#"
  • />

As you can see, the Excel data is defined in a TABLE XML node. The nodes within the Table element are fairly self-explanatory. The Data element defines what data type Excel will use to interpret the data. Each cell can take a formatting option (a CSS Style defined in the Styles nodes) to help format the data. Notice that we are taking our Rating cell to a single decimal place and that our boolean columns are being output in Yes / No format. These formatting options are all defined by the Styles.

Then, once I have the data stored up, I trim it and remove the white space between tags. Then, I convert it to binary and stream via the CFContent's Variable attribute (the variable attribute requires binary, hence the conversion). The use of the variable attribute automatically resets the content buffer so don't worry about any previous white space that was created. Notice also that I am telling the browser to use the downloaded document as if it were a Microsoft Excel document by setting the CFContent type to "application/msexcel".

Now, granted, this is a bit more involved than the HTML version, but as you can see, once you understand the layout, it's not very complicated. What IS complicated is debugging. If you do get something wrong, the document just doesn't render. It says that it dumps information into a Log, but I cannot find the Log at the location which it reports to have used. Oh well.

In the next demo, I will try to add some more cell formatting and make use of some cell formulas.

Download Code Snippet ZIP File

Post Comment  |  Ask Ben  |  Other Searches  |  Print Page




Reader Comments

Jan 8, 2007 at 8:48 AM // reply »
95 Comments

Ben,

that's nice. Thanks! I've done a similar thing with C#. The one thing to note when using XML to generate Excel sheets is that the user has to have at least Office/Excel 2003 to be able to read the Excel XML files. Apparently previous versions of office did not support XML Excel sheets.


Jan 8, 2007 at 9:06 AM // reply »
7,572 Comments

Boyan,

That is true, although I want to say there is a version of Excel from 2002 that will support this (but I might just be making that up). I figure though, that it has been 4 years... time to upgrade if you haven't already. And honestly, if you are doing this for a client, most clients are either willing to upgrade to a new Office (if that is required for the project) or already have it due to the corporate environment. But then again, I have pretty flexible clients most of the time, so that might not be a good sample of the population.

Also, I am not sure how this works in Open Office.

What I do know is that ColdFusion can read in this Microsoft Excel data using XmlParse() and convert it to an internal XML document object. That is way cool and is perhaps a huge selling point over the HTML table version.


Jan 8, 2007 at 9:49 AM // reply »
19 Comments

OpenOffice uses the Open Document Format as it's native document format. OASIS has recently adopted it as an XML standard for representing office-style documents, and rumor has it that the next version of Microsoft Office will include support for Open Document, there is already a Microsoft-sponsored effort to build a ODF->OfficeXML translator.

I haven't tried it yet, but there's a bunch of XSL files at their sourceforge site that might be possible to use with ColdFusion to translate ODF -> OfficeXML and back again.

Open Doc Format: http://www.oasis-open.org/committees/tc_home.php?wg_abbrev=office#technical

ODF Translator: http://sourceforge.net/projects/odf-converter/


Jan 8, 2007 at 10:28 AM // reply »
30 Comments

In a previous gig, we had very picky requirements for some financial reports for a government agency in the UK (alas, I was never sent over there like so many of my coworkers). The formatting, tabs, and formulas were a pain in the butt to create dynamically, so I created the files in Excel, saved them as XML files, and used a ColdFusion script to parse and replaced placeholders in the files with transaction data from the database. The results were saved in another directory, and voila! A perfectly formatted report with all worksheets, formatting, and formulas intact.

While minor tweaks to the excel template could be made without getting IT involved, the big drawback was scalability. As the reports became larger and larger (certain sections repeated and/or grouped, depending on how many financial transactions were involved), it became a very unwieldy process. In hindsight, it was not a good solution to that particular problem, but it might be useful elsewhere. Live and learn.


Jan 8, 2007 at 12:06 PM // reply »
1 Comments

I am hoping you will help me out. I am trying to get this to work in 6.1 and I can not seem to figure it out. since I don't have the variable attribute of the cfcontent tag; I am trying to find a work around. I have been playing with variations of something like this:

<cfset the_file = ToBinary(ToBase64(strXmlData.Trim().ReplaceAll( '>\s+', '>' ).ReplaceAll( '\s+<', '<' ) ) ) />

<cffile action="write" file="#expandPath('.')#/basic.xml" output="#the_file#" />

<cfheader name="content-disposition" value="attachment; filename=basic.xml" />

<cfcontent type="application/vnd.ms-excel" file="#expandPath('basic.xml')#" />

for the output, but I am only getting text in IE and and an Excel error in FF on 'Table"

Could you steer me in the right direction?

Thank you


Jan 8, 2007 at 1:01 PM // reply »
7,572 Comments

Ron,

You are very close. Since you are writing to a text file (XML file) you don't need to convert to binary. I only had to do that because the Variable attribute only accepts binary. For your thing, you can put the line:

strXmlData.Trim().ReplaceAll( '>\s+', '>' ).ReplaceAll( '\s+<', '<' )

directly into the output of the CFFile tag:

<cffile action="WRITE"
output="#strXmlData.Trim().ReplaceAll( '>\s+', '>' ).ReplaceAll( '\s+<', '<' )#" ...... />

Give that a go and it should work fine. Let me know if you have any other problems.


Jan 8, 2007 at 1:02 PM // reply »
7,572 Comments

Jeremy,

It sounds like you were creating very complicated documents. I am not sure, though, how things would not scale well. Granted I just found out about this XML stuff, but it seems that as long as you have set formats for the documents, scaling (ie. number of records) wouldn't be an issue?


Jan 8, 2007 at 1:26 PM // reply »
30 Comments

Yes, they were fairly complicated documents. The scaling issues came into play with the repeatable sections of the spreadsheets. There were placeholder markers for the beginning and end of the repeatable sections. Within these sections were placeholders for the data from the database. How many times these sections were repeated depended on the number and types of transactions. A few thousand transactions a day was fine, but once we got into a few hundred thousand per day, things got a little slow. I later went back and optimized the code significantly, which helped a bit. Granted, with the sheer volume of data being processed daily, I have a feeling that generating those spreadsheets would have been a bear any way you cut it.


Jan 8, 2007 at 1:28 PM // reply »
7,572 Comments

Most definitely a bear... and when the Row Count and the Column Count have to match up exactly, I am sure it was a nightmare dealing with dynamic / repeatable content areas. Sounds horrible to me :)

For simple documents, though, I am gonna give it a shot and see where it leads me.

Thanks for all of your feedback!


Jan 8, 2007 at 4:07 PM // reply »
5 Comments

Ben-
My coworker Matt has been using this XML method to create some pretty sick looking Excel workbooks. I mentioned that you couldn't find the error log file and he said "oh, it's here:"

C:\Documents and Settings\{username}\Local Settings\Temporary Internet Files\Content.MSO

Just sub in your username and that'll do


Jan 8, 2007 at 4:12 PM // reply »
7,572 Comments

Steve,

I appreciate that, but that's the same thing my Excel tells me ... and I still can't locate it :(

OH CRAP! It's a hidden folder. I just found it. Thanks! Stupid windows trying to protect me from myself :) Thanks!

Hey, the debugging information in this ain't half bad. Sweet.


mo
Feb 15, 2007 at 2:44 PM // reply »
1 Comments

Any idea on how to add an image to a cell w/ schemaML? Attempted to add an image using the following html namespace: xmlns:html="http://www.w3.org/TR/REC-html40
(image source pointed to an absolute path).

The xml generated by my program looks something like this:

<Cell>
<Data ss:Type="String">
this is a test
<html:Img Src="https://dev-server/test/icon.gif"/>
</Data>
</Cell>

The excel file doesn't produce any errors and opens up successfully. However that line referring to the image is completely ignored by the excel file (renders a blank cell or in this case only the test string). Just to test other html tags in html namespace, applied the html font tag to change the font style in a cell and it rendered that properly to the excel file.

(Also tried converting the image to a binary object but not sure how to write it so it would be recognized by the excel file ...)

Any ideas?


Feb 15, 2007 at 4:54 PM // reply »
7,572 Comments

Mo,

I have not tried this myself. You should create an XLS and put in am image. Then save that file (via Excel) as an XML document and open it up. My guess is that the graphic is stored as some sort of Base64 encoding (looks like about a 100 lines of random letters).


Rob
Apr 5, 2007 at 10:32 AM // reply »
8 Comments

It should be noted that this will not work unless you are using Excel 2002 or later. That is when Microsoft added XML support to Excel. While on the face it sounds irrelevant, consider that most Federal Government agencies are still using Windows 2000 and Office 2000 on a wide-scale basis. That's the struggle I had to overcome after being initially thrilled about the prospects.

All that being said, this has a ton of potential. One of the pitfalls of using HTML tables to create an Excel file is that the resulting Excel file usually looks plain, if not trashy. There also is no support for multiple tabs and Excel formulas. This XML-based approach changes all that. It allows us now to have the client design a spreadsheet exactly how they want it, look and feel, formulas, etc. and with a quick Save As, we have the template to build our dynamic Excel file from. A couple of hours of XML cleanup and proper placement of variable placeholders and we can give them exactly what they want, with no compromises.

Thanks for this tip!


ac
Jul 13, 2007 at 3:22 PM // reply »
1 Comments

I don't know if Mo has resolved his issue with embedding images in the XML file. I have the same problem. I tried inserting an image to an Excel file, saved it as .xml and checked to see what the resulting XML code is, but there is no indication about the image.

I like the XML schema for Excel coz it allows you to have much of the functionality of Excel, however, I've been stumped by my inability to embed images to the generated Excel file.


Jul 13, 2007 at 3:28 PM // reply »
7,572 Comments

@AC,

The image might be stored in base64, which would look like a few thousand characters in a row (usually broken onto a new line every X number of characters)... see anything like that in the XML?


Aug 4, 2008 at 4:25 PM // reply »
6 Comments

Ben,
Great Snipplets man. But I've converted the QueryNew( line to an actualy cfquery, wrapped the <cfset for the row data in a cfoutput, cfloop to set all the parameters, and esentially that is all that has changed,
and I get the following Error when I try to open the xls in excel:

Table.

I opened the error log and it displays this:
XML ERROR in Table
REASON: Bad Value
FILE: C:\DOCUME~1\jharvey\LOCALS~1\Temp\twc_basic.xml-1.XLS
GROUP: Cell
TAG: Data
VALUE: TXML PARSE ERROR: Missing end-tag
Error occurs at or below this element stack:
<ss:Workbook>
<ss:Worksheet>
<ss:Table>
<ss:Row>
<ss:Cell>
<ss:Data>
<ss:kkg>

Did I do something wrong?


Aug 4, 2008 at 4:28 PM // reply »
6 Comments

Just thought that before you asked here's my code:

<cfquery name="quiz_results" datasource="#datasrc#">
select id, user_name, timestamp, q1, q2, q3 from webos_bmi_quiz
</cfquery>

<cfset add_row ='#quiz_results.recordcount#'>
<!--- Add rows to query. --->
<cfset QueryAddRow( quiz_results, #quiz_results.recordcount# ) />

<!--- Set row data. --->
<cfoutput>
<cfloop query="quiz_results">
<cfset quiz_results[ "id" ][ 1 ] = JavaCast( "string", "#id#" ) />
<cfset quiz_results[ "user_name" ][ 1 ] = JavaCast( "string", "#user_name#" ) />
<cfset quiz_results[ "timestamp" ][ 1 ] = JavaCast( "string", "#timestamp#" ) />
<cfset quiz_results[ "q1" ][ 1 ] = JavaCast( "string", "#q1#" ) />
<cfset quiz_results[ "q2" ][ 1 ] = JavaCast( "string", "#q2#" ) />
<cfset quiz_results[ "q3" ][ 1 ] = JavaCast( "string", "#q3#" ) />
</cfloop>
</cfoutput>

<Worksheet ss:Name="Quiz Results">
<Table ss:ExpandedColumnCount="#ListLen( quiz_results.ColumnList )#" ss:ExpandedRowCount="#(quiz_results.RecordCount + 1)#" x:FullColumns="1" x:FullRows="1">
<Column ss:Index="1" ss:Width="30" />
<Column ss:Index="2" ss:Width="100" />
<Column ss:Index="3" ss:Width="42" />
<Column ss:Index="4" ss:Width="84" />
<Column ss:Index="5" ss:Width="66" />
<Column ss:Index="6" ss:Width="70" />
<Row>
<Cell>
<Data ss:Type="String">ID</Data>
</Cell>
<Cell>
<Data ss:Type="String">User Name</Data>
</Cell>
<Cell>
<Data ss:Type="String">Date Taken</Data>
</Cell>
<Cell>
<Data ss:Type="String">Q-1 Results</Data>
</Cell>
<Cell>
<Data ss:Type="String">Q-2 Results</Data>
</Cell>
<Cell>
<Data ss:Type="String">Q-3 Results</Data>
</Cell>
</Row>
<cfloop query="quiz_results">
<Row>
<Cell>
<Data ss:Type="Number">#quiz_results.id#</Data>
</Cell>
<Cell>
<Data ss:Type="String">#quiz_results.user_name#</Data>
</Cell>
<Cell ss:StyleID="ShortDate">
<Data ss:Type="DateTime">#DateFormat( quiz_results.timestamp, "yyyy-mm-dd" )#T#TimeFormat( quiz_results.timestamp, "HH:mm:ss.l" )#</Data>
</Cell>
<Cell>
<Data ss:Type="String">#quiz_results.q1#</Data>
</Cell>
<Cell>
<Data ss:Type="String">#quiz_results.q2#</Data>
</Cell>
<Cell>
<Data ss:Type="String">#quiz_results.q3#</Data>
</Cell>

</Row>

</cfloop>

</Table>
</Worksheet>

</Workbook>

</cfoutput>
</cfsavecontent>


Aug 5, 2008 at 3:36 PM // reply »
6 Comments

Nevermind,
One of the Usernames inputted in the database had < & > in it (apparently it was inputted before I added my stripHTML command on the entry form.

Sorry for any issues, or hours of scratching one's head.

-James


Aug 5, 2008 at 4:33 PM // reply »
6 Comments

Please disregard last post, Error is still occuring.

Any thoughts?


Aug 7, 2008 at 2:08 PM // reply »
7,572 Comments

@James,

Did the XmlFormat() suggestion help at all?


S
Oct 30, 2008 at 11:00 AM // reply »
1 Comments

I have tried to implement this but instead of a query I am using a stored procedure. But everytime I try to ope the xml.xls files I am getting an error worksheet seetings. I am not sure what is wrong with the setting. Can any one please help.

Thanks!


Oct 30, 2008 at 11:02 AM // reply »
7,572 Comments

@S,

I was getting an error once when I tried to create more that 65,000 record that way. Excel can only handle so much data. As far as errors, though, debugging the XML can be EXTREMELY hard.

You might want to checkout my POI Project that now has XML-style ColdFusion custom tags:

http://www.bennadel.com/projects/poi-utility.htm


Jan 20, 2009 at 4:41 PM // reply »
3 Comments

I commented out your qMovie query and data set up and added:
<cfquery name="qMovie" datasource="admi-prod">
select dealer_id id, dealership_name name, termination_date date_watched, primary_manufacturer_id rating, 0 has_fighting, 1 has_boobies
from dealer_master
where dealership_name like '%st%'
and rownum <= 5
</cfquery>
and now when I run the page I just get the query data dumped. Just one long string of text. Why the difference?


Jan 20, 2009 at 4:46 PM // reply »
7,572 Comments

@Kate,

Is it asking your to save the XML document? Or is it just display it? There might be an error in the XML that is being generated? You might want to wrap your individual field output in XmlFormat() method calls?


Jan 21, 2009 at 9:40 AM // reply »
6 Comments

@Kate:
I've tried to on several occasions to use the code Ben mentions here. I've had no success with it, however I did write a CF Custom Tags that works magnificently.

It's located:
http://alteredpixels.net/post.cfm/coldfusion-export-to-excel/

It even includes cell formatting, multiple tabs, etc.
Check it out.


Feb 17, 2009 at 11:50 AM // reply »
5 Comments

Hi, thanks for the article. I'm using Excel 2007 and when I try giving the file an xml extension, it automatically appends .xls (so it becomes basic.xml.XLS) This causes Excel to display the message "the file you are trying to open is in a different format than specified by the file extension." It still works but the message is annoying. Is there any way to prevent this?


Feb 18, 2009 at 8:58 AM // reply »
7,572 Comments

@Candice,

Are you talking about generating the XML document programmatically using ColdFusion? Or are you talking about saving a document from within Excel?


Feb 20, 2009 at 11:34 AM // reply »
4 Comments

"It says that it dumps information into a Log, but I cannot find the Log at the location which it reports to have used. Oh well."

From my experience using FireFox, you can paste the path given to you by the error message into the address bar of your browser and it will display the log file as text.

Common issues are improperly formatted cells and not accounting for the expanded row count correctly.

Keep up the great work Ben, I really appreciate your site and your insight.


Feb 20, 2009 at 11:52 AM // reply »
7,572 Comments

@Vic,

Thank man, good tip. I'll be sure to give that a try next time it comes up.


Mar 2, 2009 at 9:39 AM // reply »
5 Comments

I'm talking about generating the XML document programmatically using ColdFusion. It happens even when using the same code in the example.


Mar 2, 2009 at 9:53 AM // reply »
4 Comments

When I create a test.cfm and use the sample code I just get the basic.xml file. I am using excel 2007 as well.

Excel will always give you that notification if the file being opened is not a true xls. The only way that I have found around this was using VB.NET, and even then the solution was not perfect.


Mar 2, 2009 at 7:19 PM // reply »
7,572 Comments

I don't have Office 2007 so I cannot test this stuff.


Mar 19, 2009 at 9:34 AM // reply »
2 Comments

Hey all,
this may be what I need to use, but I am not totally sure. So here is my issue:
My supervisor (not CF smart at all), does not like the html based report I am generating. In HTML if the page is more than one page, if I try to print it, the first page is blank and data is missing. So, I try to use cf to export to excel. Here is his issue, the exported file does not work like excel. He actually has to hit print and set up the print features to print to landscape and fit to page. Will this concept above help me solve this issue so I can get him off my back...wait that will still cause him to be a pain, but at least I can solve this problem, and learn something new along the way?

thanks
Dan


Mar 22, 2009 at 6:12 PM // reply »
1 Comments

@Candice,

Did you ever find a work around to the "different format specified by the file extension" warning?


Apr 10, 2009 at 9:59 AM // reply »
4 Comments

Nice easy to follow tutorial Ben - cheers.

I have a question though - I am trying to out put data for specific members using tabs so the above example works well for me. My problem is that I actually want to include 2 tables of data in the one worksheet for each member.

Basically the first table will all be member details and the second table would specific of that members account shown. I tried the 2 table approach but quickly learned that didn't work and I am going to plug away and trial and error some kinda solution if possible.

If anyone has any input on this it would be much appreciated.

Cheers.


Apr 13, 2009 at 8:52 AM // reply »
7,572 Comments

@Neil,

Try creating your desired document in Excel and then exporting as XML to see what it creates - that's basically what I did.


Apr 20, 2009 at 9:29 AM // reply »
4 Comments

Cheers for the pointer Ben - I just did some creating of the doc in Excel and then viewed the source with notepad and was able to extract what I needed.

Cheers.


Apr 20, 2009 at 9:25 PM // reply »
7,572 Comments

@Neil,

That's awesome. Glad it worked.


Jun 5, 2009 at 2:18 PM // reply »
5 Comments

@Mike,

No I never did find a workaround.

Most users have figured out that they can just click 'Yes' but it's still annoying.


Jun 15, 2009 at 8:18 PM // reply »
11 Comments

'Real Genius' doesn't have boobies? I could have sworn it did! Oh well... time to take it off my Netflix list.


Jun 16, 2009 at 8:30 AM // reply »
7,572 Comments

@David,

Let's not be drastic - it's still an awesome movie!


Jun 24, 2009 at 2:11 PM // reply »
4 Comments

First of all, excellent blog post and comments...

i use C# to make XML Excel files when comes to reports with dynamic columns... got through the same issues you are commenting, Where's the log?, How many rows and cols are? and finally making the design usefull to the user: If there were too many columns break to another page or if it doesn't fit on a A4 paper then add some new page in the same worksheet, etc.

But, the one thing i need, actually, is the image... got ideas?

if i can't walk through this last problem... i will have to change to another format... and to be honest, xml for Excel has been a real challenge yes, but i "luv" the results :)

Regards


Jun 24, 2009 at 2:31 PM // reply »
7,572 Comments

@Jorge,

You'd have to create an XLS file and the export as XML and see what it does. Not sure if it would embed it via Base64 or if it would actually save the image externally and then link to it. If its embedded base64, then you are in a good place. If it's externally linked, then you're gonna have packaging problems :(


Jun 24, 2009 at 6:16 PM // reply »
4 Comments

Thanks Ben, actually, i thought that before, but i don't see eny of it inside the xml file.

i have tried to insert and link the image (which is an option of the image insert dialog) but without luck...

i have been saving the xls as xml and then when i open the xml file it doesn't show the image no matter what image format i use... anyway, i'll report back to you if i find something :)


Jun 24, 2009 at 7:22 PM // reply »
4 Comments

Wow!, looks like MS-Word has some pretty complex options for design... i will merge this with an excel table to get both features...

for now, the problem of the image is resolved with word saved as xml.

Best wishes


Jun 25, 2009 at 8:34 AM // reply »
7,572 Comments

@Jorge,

Yeah, MS has some CRAZY document formatting internally when you export. Show's you how nuts it is behind the scene. Good luck!


Jul 28, 2009 at 5:01 AM // reply »
1 Comments

Ben ,

This is indeed helpful.
But I am looking for including images in Excel.
I opened the XML file in Excel and embed a image in it.Then tried to save it as XML Spreadsheet , It gives me warning that Autoshapes, charts etc will not be saved in this format.

Can you suggest some workaround to achieve this.


Aug 5, 2009 at 7:34 PM // reply »
7,572 Comments

@Aradhana,

If saving an Excel document as XML discards images / shapes, I am not sure that you can go about it in this approach.


Nov 25, 2009 at 8:20 AM // reply »
17 Comments

Hi Ben,

I need to implement the export to excel functionality.

I have multiple rows in a screen....Select any one row and then export to excel, then a certain file is exported to excel.This is simple as I did it.

But now I need to impelemnt for multiple files.........I mean the user can select multiple rows and then hit the excel button...........Then he should be able to downlaod multiple excel reprots in one go...........

Please Ben help.............

Rgds,

Abhi


Dec 15, 2009 at 7:59 PM // reply »
2 Comments

How To Repeat Rows at Top on each page while taking print in excel


Dec 15, 2009 at 11:29 PM // reply »
1 Comments

You can select the option of repeating row headers in PageSetup and save the file as xml.

Open the xml in any editor and see what you need to write in xml to achieve this.


Dec 16, 2009 at 12:29 AM // reply »
2 Comments

I want to repeat rows at top on each page through code and not manually. On creating the excel file the option should be enabled


Jan 5, 2010 at 9:52 AM // reply »
7,572 Comments

@Adith,

@Aradhana is correct - you need to implement it in a standard XLS file and then export it to see how it is done in XML. I don't know this off hand.

@Vicky,

I would suggest using CFZip (if you are CF8+) and zip the files together before streaming them to the client. Are you on CF8 or CF9?


Jan 10, 2010 at 9:46 AM // reply »
44 Comments

@Ben, I skimmed through the comments, not sure if anyone has mentioned this or not, but I took your example and made a change on line 297 which was to change the basic.xml to basic.xsl to see if it would open it and it worked fine. So if you were creating an excel file and wanted it available as whatever.xls, it's totally doable. I'm using Office 2K3, so it worked with, not sure about other versions. This could fix the issue with Offic 2K7 mentioned above about the appending of the .xls to the file like basic.xml.xls mentioned above.


Jan 12, 2010 at 1:50 AM // reply »
5 Comments

I see that the XML is being displayed in the browser directly rather than getting the Open/Saev Dialog...

Can it be because of any of the following reasons viz.,

#1. File Size is huge
#2. Connection speed

-Naveen


Jan 16, 2010 at 5:37 PM // reply »
7,572 Comments

@Hatem,

Good idea! Yeah, as long as the data in the file is "good" for the given application, I think tweaking the file extension might just work.

@Naveen,

I have not seen that happen; but it's been a while since I've played around with this.


Jan 21, 2010 at 11:47 AM // reply »
2 Comments

Can we append/add sheet in Excel document?

I have a big data to export in excel and CFsaveContent is limited by its memory.


Jan 21, 2010 at 11:58 AM // reply »
5 Comments

Meenakshi -
look in to using the the java stringbuffer classes to generate the file much more efficiently. i ran into memory limits with cfsavecontent too.

either that or upgrade to ColdFusion 9 and enjoy a much, much simpler way to manipulate spreadsheets!


Jan 21, 2010 at 1:19 PM // reply »
2 Comments

@Steve

Can you give me some example/link so that I get idea how it works

Thanks for your help


Jan 21, 2010 at 9:06 PM // reply »
7,572 Comments

@Meenakshi,

@Steve is right - you can look into creating a string buffer; also, you would write the file to disk a bit at a time, appending to the file after X records are written.


Jan 22, 2010 at 2:48 AM // reply »
11 Comments

@Ben, @Steve,

Could this "String Buffer" (sounds like something from Star Trek) be used when reading an extra large excel file from the disc say using the POI utility? Or can you only do it when writing something to the disc?

The reason I ask, is that I've been trying to use the POI utility to read some exceptionally large excel files but the server crashes every time i try and read a file greater than 15-20mb.


Jan 22, 2010 at 11:53 AM // reply »
5 Comments

@David - there's one way to find out :-)

(ps - 'exceptionally large' is right! wow!)


Jan 22, 2010 at 11:56 AM // reply »
5 Comments

@David,

quick check of some old code - BufferedReader is what you'd be looking to use.

Steve


Jan 23, 2010 at 9:00 PM // reply »
11 Comments

@steve,
thanks... I'll see if I can find some introductory information on BufferedReader. Do you happen to have any recommended resources I should check out... possibly places that have good sample code?


Jan 25, 2010 at 2:28 PM // reply »
1 Comments

Your example about creating Excel Documents with Coldfusion and XML addressed almost exactly what I need. However, I have two questions: how do I enter multi-line content in the Excel cells, without Excel making multiple rows. Secondly, can a spreadsheet be created with automatically-determined column widths? (Your example specified each one.)


Jan 25, 2010 at 2:34 PM // reply »
5 Comments

@Danny -
Ben answered the first one in an earlier blog post - I've used his suggestion and it works just fine.

http://www.bennadel.com/blog/1095-Maintaining-Line-Breaks-In-An-HTML-Excel-File.htm

as for the second...not sure!


Jan 25, 2010 at 9:07 PM // reply »
7,572 Comments

@David,

Try taking a look at this post that I have on reading huge XML files, from a while back. It uses regular expressions and a buffered reader to parse chunks of the XML file at a time.

http://www.bennadel.com/blog/1345-Ask-Ben-Parsing-Very-Large-XML-Documents-In-ColdFusion.htm


Feb 5, 2010 at 1:43 AM // reply »
1 Comments

When Excel cant open the XML file, it does write the error out to a log. I ran ProcessMonitor and they are written out to: C:\Documents and Settings\<MyName>\Local Settings\Temporary Internet Files\Content.MSO\<DifferentNumberEachTime>.log


Feb 6, 2010 at 6:09 PM // reply »
7,572 Comments

@Jeremy,

Good call! Does it give information as to what went wrong, such as the line number or anything?


Feb 8, 2010 at 11:34 AM // reply »
5 Comments

I am trying to save the excel file instead of streaming it to the browser. The file gets created fine but when I open it, it says that it is locked for editing by 'another user.' Then it gives me the options to open it Read-Only or be Notified when the document is no longer in use. Does anyone know why this is happening and how to fix it?


Feb 8, 2010 at 1:44 PM // reply »
5 Comments

^ Nevermind that question. I figured out it has nothing to do with the way the file is created. It's caused by the program I'm using to get the files.


Feb 8, 2010 at 4:04 PM // reply »
11 Comments

@Ben,
Thanks for the tip regarding your XML article and using BufferedInputStream instead of FileInputStream. I wish excel files were written in plain text just like your XML example. Unfortunately I think they need some type of decoding in order to view the XML(ish) structure.

I decided to try and modify your POI utility to see if I could get it to use BufferedInputStream instead of FileInputStream. I modified line 871 of POIUtility.cfc to read:
LOCAL.FileInputStream = CreateObject( "java", "java.io.BufferedInputStream").Init(CreateObject("java","java.io.FileInputStream").Init(JavaCast("string",ARGUMENTS.FilePath)));

However, I think that the ExcelFileSystem object cannot handle a buffered input stream. Does the apache.poi have some way of buffering the excel input stream?


Feb 8, 2010 at 11:15 PM // reply »
7,572 Comments

@Candice,

No problem - glad you got it figured out.

@David,

To view the XLS document as an XML file, if I remember correctly, I actually opened up the document in Excel and then went "File > Save As > XML". So, there was not an encoding issue, but rather a completely separate file format.

As far as the buffered input stream with POI, I am not sure that it would make a difference; I believe POI reads in the entire file before it does anything with it. As such, I am not sure that the buffering nature would have a performance difference.


Feb 9, 2010 at 2:09 PM // reply »
11 Comments

@Ben,
Thanks for the tip. It sounds like because xlsx files aren't in plain text, the buffered reader wouldn't be able to look for any patterns anyway. XML could be the answer to my problem or possibly even a CSV file. I'll let you know how it goes. :)


Feb 10, 2010 at 10:28 PM // reply »
7,572 Comments

@David,

Gotcha. I never activated my 2007 Office suite; I tend to use to use 2003 (I'm old-school that way), so when it comes to ...X documents, I'm just shooting in the dark :)


Post Comment  |  Ask Ben

Recent Blog Comments
Mar 22, 2010 at 3:08 AM
Ask Ben: Selecting XML Attributes Given Other XML Attributes
Thanks for the response. I finally discovered that I was getting this error because I had cfsetting enablecfoutputonly="yes" in Application.cfc, and was neither setting it to false elsewhere nor brac ... read »
Mar 21, 2010 at 8:57 PM
The Bourne Ultimatum Starring Matt Damon And Julia Stiles
late to the party, but my observation is this: rewatch carefully for the platonic nature of the relationship between nicki and jason. she never flirts with him. he never comes on to her. they alway ... read »
Mar 21, 2010 at 7:40 PM
Is Simulating User-Input Events With jQuery Ever A Good Idea?
A couple of things. One you embed the initial state of of more-info in the CSS. IMHO, that behavior should be in jQuery: moreInfo.hide(); It shows that the behavior your toggling and closing is mor ... read »
Mar 21, 2010 at 3:59 PM
Exploring ColdFusion Component Runtime Class Properties And Serialization
@Elliott, according to Ben's experiment, serializeJSON() doesn't access the private data by default - it doesn't even access the getHair() method - so trying to clone a Girl.cfc via serializeJSON/des ... read »
Mar 21, 2010 at 3:49 PM
Ask Ben: Javascript String Replace Method
I'm confused a bit by what you are asking, but if had this sentence: The color, red, is in the style statement; style: red;. and wanted to remove all or change all of the commas, colons, and semi-c ... read »
Mar 21, 2010 at 3:13 PM
Ask Ben: Javascript String Replace Method
I am trying to make a java program to count the number of times that these punctuation marks occur in a body of text: , : ; . ! - ' " ? / \ I am using this piece to ferret out the commas: numcommas ... read »
Mar 21, 2010 at 11:13 AM
A New Wrist Pain
@chiropractor suwanee, Spoken like someone trying to sell something. Other than for minor, temporary relief from some back pain, chiropractic treatment is nothing but placebo effect and quackery. ... read »
Mar 21, 2010 at 6:32 AM
ColdFusion CFPOP - My First Look
Apologies... The field name in the db for C. is "BounceCode" It stores the code / message which is returned in the email. Sorry for the confusion. ... read »