CFXL 2.0 Released

For those of you that aren't familiar with my CFXL project. It started many years ago as a very basic API to modify an existing spreadsheet. Well, I've been working on CFXL for a while now. I recently came to the realization that I needed to change the underlying API I was using from JExcel API to Apache POI if CFXL was ever going to be truly powerful. Well, now CFXL has been updated under the covers to work just the way so many people wanted it to. :)

First, parsing is now built in and really trivial. Here is some example code that will read every cell in a spreadsheet and output it.

<cfscript>
    cfxl = createObject("component","com.jasondelmore.cfxl.cfxl").init("#GetDirectoryFromPath(GetCurrentTemplatePath())#/svbn.xls");
    cfxl.setSheet(0);
    
    writeoutput("<table>");
    for (i=1;i
<=cfxl.getSheet().getLastRowNum();i++) {
        writeoutput("<tr>");
        for (j=1;j<=cfxl.getRow().getLastCellNum();j++) {
            writeoutput("<td>");
            writeOutput(cfxl.getCellValue(j,i));
            writeoutput("</td>");
        }
        writeoutput("</tr>");
    }
    writeoutput("</table>");
    
    cfxl.closeWorkbook();
</cfscript>

Or... how about some code for creating a new spreadsheet from scratch using tag syntax.

<cf_xl action="view">
    <cf_xlparam column="A" row="13" value="My Example Excel File"/>
    <cf_xlparam column="A" row="14" value="100.5"/>
    <cf_xlparam column="A" row="15" value="200.4"/>
    <cf_xlparam column="A" row="16" value="300.3"/>
    <cf_xlparam column="A" row="17" value="400.2"/>
    <cf_xlparam column="A" row="18" value="500.1"/>
</cf_xl>

Or... how about doing the same, but this time calling the CFXL API directly, and using some of the functions that are in the POI API directly as well...

<cfscript>
    cfxl = createObject("component","com.jasondelmore.cfxl.cfxl").init("#GetDirectoryFromPath(GetCurrentTemplatePath())#/demo.xls");
    cfxl.setSheet("CFInsider Demo");
    //cfxl.setSheet(1);
    cfxl.setCell("A",13,"My Example Excel File");
    cfxl.setCell("A",14,100);
    cfxl.setCell("A",15,100);
    cfxl.setCell("A",16,200);
    cfxl.setCell("A",17,500);
    cfxl.setCell("A",18,1000);
    cfxl.setCell("B",36,now());
    cfxl.setCell("B",37,True);
    
    cfxl.setCell("A",38,cfxl.getCellValue("A",16));
    // Note, you can go right in and grab the current sheet and use all of the cfxl APIs directly... same goes for cells and workbooks.
    cfxl.getSheet().setZoom(4, 1);
    cfxl.viewWorkbook();
</cfscript>

CFXL is now extraordinarily powerful and flexible. Please give it a try, tell me what you like and what you don't like!

CFXL is on RIAForge at http://cfxl.riaforge.org/ :)

Best, Jason

Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Bill McCahey's Gravatar Thanks!!! This is awesome!

What formats will this support?
# Posted By Bill McCahey | 2/27/09 4:03 PM
Jason Delmore's Gravatar CFXL 2.0 is built on POI 3.5 (beta 5)... hopefully the production version will come out soon. POI 3.5 supports Excel 97-2007 and also supports Excel 2007 Office Open Format (.xlsx). CFXL should parse any of those formats without any problem. At the moment, CFXL creates spreadsheets in the traditional Excel format, but it would be pretty trivial to change that if someone really wanted to create XLSX formatted files.

I've been testing this a lot recently and it is solid. I've also been using Open Office to read and create the test files so interoperability doesn't seem like it is a problem at all.

:)
# Posted By Jason Delmore | 2/27/09 4:11 PM
Roland Collins's Gravatar How does POI/CFXL handle very large spreadsheets (30 columns, 10,000 rows)? We used to use POI in one of our own components, but it would always wind up creating a memory leak on the server. Is the new POI better in this regard?
# Posted By Roland Collins | 2/27/09 6:13 PM
Jason Delmore's Gravatar I hadn't tested performance until you just asked actually. I would say the results are pretty spectacular. To test this, I setup a simple template to parse each cell and set the return value into a temp variable, with a couple of getTickCount's around that to see how long it takes. This is on an untuned CF Server running on my Macbook with 50 windows open.

I tested with 30 columns of numeric data... and then here is the number of rows and how long it took to process.
100 - 3.744 seconds
1000 - 9.422 seconds
5000 - 34.369 seconds
10000 - 63.109 seconds
20000 - 121.99 seconds

You can see the speed is better than linear up to 20,000 records. I didn't try past that.

CFXL is written with performance front and center. It reads in from a FileInputStream (which loads in the raw bytes of the file as needed... not the whole file), and a byteArrayOutputStream (which allows CFXL to output to the browser without writing to disk.)

Here's an example of the benefit of this... I tested the file with 20,000 rows and just parsed the last 1000. How much time did it take? 10.83 seconds. Skipping 19,000 rows took about 1 second, so there is almost no penalty for file size. It's just a matter of what you are doing with what you've parsed.

I bet some of you are thinking... yeah, numbers, but what about something else, like a date field?

10000 records processed in 59.481 seconds - even faster than numbers...

Please feel free to try it out, and if you find any issues, please let me know.

Cheers, :)
Jason
# Posted By Jason Delmore | 2/27/09 11:28 PM
Sebastiaan's Gravatar Uh, where is the download link?
# Posted By Sebastiaan | 2/28/09 2:53 PM
david buhler's Gravatar Download Link: http://cfxl.riaforge.org/
# Posted By david buhler | 2/28/09 7:21 PM
salvatore fusto's Gravatar Hi jason, awaresome.
i've a question: if i have marged cells, horizontally and / or vertically, may i use your cfc to set or get values in these big cells? how?
regards
salvatore
# Posted By salvatore fusto | 3/4/09 12:12 PM
salvatore fusto's Gravatar excuse me, i
i say horizonatal and / or vertical merged cells.......
regards
# Posted By salvatore fusto | 3/4/09 12:22 PM
Jason Delmore's Gravatar Hi Salvatore,

Actually, there is a simple way to do this. If you want to set or get the value of a set of merged cells, all you need to do is get or set the value of the topmost and leftmost cell.

Ie. cfxl.setCell("A",14,100) will set the value of any merged cells starting at that location (horizontally, vertically, or both).

Hope that helps, :)
Jason
# Posted By Jason Delmore | 3/5/09 5:03 PM
salvatore fusto's Gravatar yes, this help.
now i'm sure that cfxl component treats mergerd cell as excel does.
thanks and regards
salvatore
# Posted By salvatore fusto | 3/6/09 3:03 AM
Scott's Gravatar I'm having good luck reading xls files. But, when I try to read an xlsx file, the template seems to stop. Even puttting a try/catch around the cfxl init() doesn't do anything. Is there anything different I need to do for xlsx files?
# Posted By Scott | 5/14/09 7:51 PM
Scott's Gravatar Is there anything special I need to do to get xlsx files to be parsed? XLS files work fine, but XLSX files just seem to stop processing. No error is thrown. It just stops. Thanks!
# Posted By Scott | 5/14/09 7:53 PM
Jason Delmore's Gravatar Hmmm... I will have to test that out. The API I used should read XLSX as well, but admittedly I have not had a chance to test it (I don't have Office 2007). If you send me the file you are trying to parse, I will give that a shot and see what needs to be changed. :)
# Posted By Jason Delmore | 5/15/09 12:15 PM
Tim Knell's Gravatar I got support for xlsx to work.

I just had to add the following jar files to the javaloader:
xmlbeans.jar
ooxml-schemas.jar
dom4j.jar

I found this out after reading this post:
http://article.gmane.org/gmane.comp.jakarta.poi.us...

So far so good. Thanks a lot Jason!
# Posted By Tim Knell | 11/24/09 3:41 PM
Joshua Spangler's Gravatar Tim Knell,
Regarding getting support for .xlsx files, how did you add the 3 jar files to the javaloader?

I downloaded the 3 jar files to:
com\jasondelmore\cfxl
And added the 3 files to the cfxl.cfc file:
      <cfset paths[5] = thisPath & "xmlbeans-2.3.0.jar"/>
      <cfset paths[6] = thisPath & "poi-ooxml-schemas-3.6-20091214.jar"/>
      <cfset paths[7] = thisPath & "dom4j-1.6.1.jar"/>

When I run it, I get a 500 JRun Server error:
ROOT CAUSE:
java.lang.NoClassDefFoundError: org/apache/log4j/Logger

Thank you for any ideas or support,
Josh
# Posted By Joshua Spangler | 2/3/10 4:57 PM
BlogCFC was created by Raymond Camden. This blog is running version 5.9. Contact Blog Owner