微软在桌面系统上的成功,令咱们不得不大量使用它的办公产品,如:Word,Excel。时至今日,它的源代码仍然不公开已封锁了咱们的进一步应用和开发。在咱们实际开发企业办公系统的过程当中,经常有客户这样子要求:你要把咱们的报表直接用Excel打开。或者是:咱们已经习惯用Excel打印。可是这种的客户需求在j2ee环境的环境下怎么实现?java
一.Java用POI操做Excel文件web
Apache的Jakata项目的POI子项目的HSSF接口能够处理MS Excel(97-2002)对象。它不象咱们仅仅是用csv生成的没有格式的能够由Excel转换的东西,而是真正的Excel对象,你能够控制一些属性如sheet,cell等等。另外,无锡永中Office的实现方案也是纯java的解决方案,不过那也是彻底商业的产品,并非公开代码项目。其实,从开发历史的角度讲,在80年代中期starOffice的原做者在德国成立了StarOffice suite公司,而后到1999年夏天starOffice被sun收购,再到2000年6月starOffice5.2的发布;而且从starOffice6.0开始,starOffice创建在OpenOffice的api的基础上,这个公开代码的office项目已经进行了很长的时间。虽然那是由C++写的,可是POI的代码部分也是由openOffice改过来的。apache
HSSF提供给用户使用的对象在org.apache.poi.hssf.usermodel包中,主要部分包括Excell对象,样式和格式,还有辅助操做。有如下几种对象:编程
HSSFWorkbook excell的文档对象api
HSSFSheet excell的表单服务器
HSSFRow excell的行并发
HSSFCell excell的格子单元分布式
HSSFFont excell字体性能
HSSFName 名称测试
HSSFDataFormat 日期格式
在poi1.7中才有如下2项:
HSSFHeader sheet头
HSSFFooter sheet尾
和这个样式
HSSFCellStyle cell样式
辅助操做包括
HSSFDateUtil 日期
HSSFPrintSetup 打印
HSSFErrorConstants 错误信息表
//利用Java 建立Excel文档 import org.apache.poi.hssf.usermodel.*; import java.io.FileOutputStream; import java.io.IOException; public class CreateCells { public static void main(String[] args) throws IOException { HSSFWorkbook wb = new HSSFWorkbook(); //创建新HSSFWorkbook对象 HSSFSheet sheet = wb.createSheet("new sheet"); //创建新的sheet对象 // Create a row and put some cells in it. Rows are 0 based. HSSFRow row = sheet.createRow((short)0); //创建新行 // Create a cell and put a value in it. HSSFCell cell = row.createCell((short)0); //创建新cell cell.setCellValue(1); //设置cell的整数类型的值 // Or do it on one line. row.createCell((short)1).setCellValue(1.2); //设置cell浮点类型的值 row.createCell((short)2).setCellValue("test"); //设置cell字符类型的值 row.createCell((short)3).setCellValue(true); //设置cell布尔类型的值 HSSFCellStyle cellStyle = wb.createCellStyle(); //创建新的cell样式 cellStyle.setDataFormat(HSSFDataFormat.getFormat("m/d/yy h:mm")); //设置cell样式为定制的日期格式 HSSFCell dCell =row.createCell((short)4); dCell.setCellValue(new Date()); //设置cell为日期类型的值 dCell.setCellStyle(cellStyle); //设置该cell日期的显示格式 HSSFCell csCell =row.createCell((short)5); csCell.setEncoding(HSSFCell.ENCODING_UTF_16); //设置cell编码解决中文高位字节截断 csCell.setCellValue("中文测试_Chinese Words Test"); //设置中西文结合字符串 row.createCell((short)6).setCellType(HSSFCell.CELL_TYPE_ERROR); //创建错误cell // Write the output to a file FileOutputStream fileOut = new FileOutputStream("workbook.xls"); wb.write(fileOut); fileOut.close(); } }
经过这个例子,咱们能够清楚的看到xls文件从大到小包括了HSSFWorkbook HSSFSheet HSSFRow HSSFCell这样几个对象。咱们能够在cell中设置各类类型的值。尤为要注意的是若是你想正确的显示非欧美的字符时,尤为象中日韩这样的语言,必须设置编码为16位的便是HSSFCell.ENCODING_UTF_16,才能保证字符的高8位不被截断而引发编码失真造成乱码。
其余测试能够经过参考examples包中的测试例子掌握poi的详细用法,包括字体的设置,cell大小和低纹的设置等。须要注意的是POI是一个仍然在完善中的公开代码的项目,因此有些功能正在扩充、有些功能还在修改。如HSSFSheet的getFooter() getHeader()和setFooter(HSSFFooter hsf) setHeader(HSSFHeader hsh)是在POI1.7中才有的,而POI1.5中就没有。运行测试熟悉代码或者使用它作项目时请注意POI的版本。
由于POI还不是一个足够成熟的项目,因此有必要作进一步的开发和测试,确认生成的Excel是否能知足用户挑剔的需求。
二.Java用PageOffice操做Excel文件
PageOffice是一个实践了分布式计算设计思想的组件,提供的方案并非纯服务器端的Excel解决方案,对文件的处理放到了客户端来进行。用PageOffice开发须要的引入的文件只需一个pageoffice4.4.0.3.jar(以具体的jar版的版本号为准)。毕竟是商业软件,提供的编程接口简洁高效。针对excel文件生成的类是:com.zhuozhengsoft.pageoffice.excelwriter
类摘要 |
|
Border |
Border 类,表明Excel中定义的边框对象。 |
Cell |
Cell 类,表明Excel中定义的单元格对象,用来填充单元格数据及控制单元格格式。 |
DataField |
DataField 类,表明PageOffice中定义的Excel表格对象里的字段对象。 |
DataFieldCollection |
DataField 字段集合,表明 com.zhuozhengsoft.pageoffice.excelwriter.Table 中当前记录行。 |
Font |
Font 类,表明Excel中定义的字体对象。 |
Sheet |
Sheet 类,表明Excel中定义的工做表对象。 |
Table |
Table 类,表明PageOffice中定义的Excel表格对象。 |
Workbook |
Workbook 类表明一个Excel文档,用来动态输出数据到Excel文档而且控制其表格格式及编辑功能。 |
excelwriter类生成excel文件的效果能够在官方提供的Sample4包里找到。如下是PageOffice官方示例代码中提供的一个彻底用程序生成预算表的demo的源代码,演示了针对Excel文档生成所提供的大部分接口。
Workbook wb = new Workbook(); // 设置背景 Table backGroundTable = wb.openSheet("Sheet1").openTable("A1:P200"); backGroundTable.getBorder().setLineColor(Color.white); // 设置标题 wb.openSheet("Sheet1").openTable("A1:H2").merge();//合并单元格 //打开表格并设置表格的行高 wb.openSheet("Sheet1").openTable("A1:H2").setRowHeight(30); Cell A1 = wb.openSheet("Sheet1").openCell("A1"); //设置单元格的水平对齐方式 A1.setHorizontalAlignment(XlHAlign.xlHAlignCenter); //设置单元格的垂直对齐方式 A1.setVerticalAlignment(XlVAlign.xlVAlignCenter); //设置单元格的前景色 A1.setForeColor(new Color(0, 128, 128)); //给单元格赋值 A1.setValue("出差开支预算"); //设置字体:加粗、大小 wb.openSheet("Sheet1").openTable("A1:A1").getFont().setBold(true); wb.openSheet("Sheet1").openTable("A1:A1").getFont().setSize(25); // 画表头 Border C4Border = wb.openSheet("Sheet1").openTable("C4:C4").getBorder(); //设置表格边框的宽度、颜色 C4Border.setWeight(XlBorderWeight.xlThick); C4Border.setLineColor(Color.yellow); Table titleTable = wb.openSheet("Sheet1").openTable("B4:H5"); //设置表格边框的样式、宽度、颜色 titleTable.getBorder().setBorderType(XlBorderType.xlAllEdges); titleTable.getBorder().setWeight(XlBorderWeight.xlThick); titleTable.getBorder().setLineColor(new Color(0, 128, 128)); // 画表体 Table bodyTable = wb.openSheet("Sheet1").openTable("B6:H15"); bodyTable.getBorder().setLineColor(Color.gray); bodyTable.getBorder().setWeight(XlBorderWeight.xlHairline); Border B7Border = wb.openSheet("Sheet1").openTable("B7:B7").getBorder(); B7Border.setLineColor(Color.white); Border B9Border = wb.openSheet("Sheet1").openTable("B9:B9").getBorder(); B9Border.setBorderType(XlBorderType.xlBottomEdge); B9Border.setLineColor(Color.white); Border C6C15BorderLeft = wb.openSheet("Sheet1").openTable("C6:C15").getBorder(); C6C15BorderLeft.setLineColor(Color.white); C6C15BorderLeft.setBorderType(XlBorderType.xlLeftEdge); Border C6C15BorderRight = wb.openSheet("Sheet1").openTable("C6:C15").getBorder(); C6C15BorderRight.setLineColor(Color.yellow); C6C15BorderRight.setLineStyle(XlBorderLineStyle.xlDot); C6C15BorderRight.setBorderType(XlBorderType.xlRightEdge); Border E6E15Border = wb.openSheet("Sheet1").openTable("E6:E15").getBorder(); E6E15Border.setLineStyle(XlBorderLineStyle.xlDot); E6E15Border.setBorderType(XlBorderType.xlAllEdges); E6E15Border.setLineColor(Color.yellow); Border G6G15BorderRight = wb.openSheet("Sheet1").openTable("G6:G15").getBorder(); G6G15BorderRight.setBorderType(XlBorderType.xlRightEdge); G6G15BorderRight.setLineColor(Color.white); Border G6G15BorderLeft = wb.openSheet("Sheet1").openTable("G6:G15").getBorder(); G6G15BorderLeft.setLineStyle(XlBorderLineStyle.xlDot); G6G15BorderLeft.setBorderType(XlBorderType.xlLeftEdge); G6G15BorderLeft.setLineColor(Color.yellow); Table bodyTable2 = wb.openSheet("Sheet1").openTable("B6:H15"); bodyTable2.getBorder().setWeight(XlBorderWeight.xlThick); bodyTable2.getBorder().setLineColor(new Color(0, 128, 128)); bodyTable2.getBorder().setBorderType(XlBorderType.xlAllEdges); // 画表尾 Border H16H17Border = wb.openSheet("Sheet1").openTable("H16:H17").getBorder(); H16H17Border.setLineColor(new Color(204, 255, 204)); Border E16G17Border = wb.openSheet("Sheet1").openTable("E16:G17").getBorder(); E16G17Border.setLineColor(new Color(0, 128, 128)); Table footTable = wb.openSheet("Sheet1").openTable("B16:H17"); footTable.getBorder().setWeight(XlBorderWeight.xlThick); footTable.getBorder().setLineColor(new Color(0, 128, 128)); footTable.getBorder().setBorderType(XlBorderType.xlAllEdges); // 设置行高列宽 wb.openSheet("Sheet1").openTable("A1:A1").setColumnWidth(1); wb.openSheet("Sheet1").openTable("B1:B1").setColumnWidth(20); wb.openSheet("Sheet1").openTable("C1:C1").setColumnWidth(15); wb.openSheet("Sheet1").openTable("D1:D1").setColumnWidth(10); wb.openSheet("Sheet1").openTable("E1:E1").setColumnWidth(8); wb.openSheet("Sheet1").openTable("F1:F1").setColumnWidth(3); wb.openSheet("Sheet1").openTable("G1:G1").setColumnWidth(12); wb.openSheet("Sheet1").openTable("H1:H1").setColumnWidth(20); wb.openSheet("Sheet1").openTable("A16:A16").setRowHeight(20); wb.openSheet("Sheet1").openTable("A17:A17").setRowHeight(20); // 设置表格中字体大小为10 for (int i = 0; i < 12; i++) {//excel表格行号 for (int j = 0; j < 7; j++) {//excel表格列号 wb.openSheet("Sheet1").openCellRC(4 + i, 2 + j).getFont().setSize(10); } } // 填充单元格背景颜色 for (int i = 0; i < 10; i++) { wb.openSheet("Sheet1").openCell("H" + (6 + i)).setBackColor(new Color(255, 255, 153)); } wb.openSheet("Sheet1").openCell("E16").setBackColor(new Color(0, 128, 128)); wb.openSheet("Sheet1").openCell("F16").setBackColor(new Color(0, 128, 128)); wb.openSheet("Sheet1").openCell("G16").setBackColor(new Color(0, 128, 128)); wb.openSheet("Sheet1").openCell("E17").setBackColor(new Color(0, 128, 128)); wb.openSheet("Sheet1").openCell("F17").setBackColor(new Color(0, 128, 128)); wb.openSheet("Sheet1").openCell("G17").setBackColor(new Color(0, 128, 128)); wb.openSheet("Sheet1").openCell("H16").setBackColor(new Color(204, 255, 204)); wb.openSheet("Sheet1").openCell("H17").setBackColor(new Color(204, 255, 204)); //填充单元格文本和公式 Cell B4 = wb.openSheet("Sheet1").openCell("B4"); B4.getFont().setBold(true); B4.setValue("出差开支预算"); Cell H5 = wb.openSheet("Sheet1").openCell("H5"); H5.getFont().setBold(true); H5.setValue("总计"); H5.setHorizontalAlignment(XlHAlign.xlHAlignCenter); Cell B6 = wb.openSheet("Sheet1").openCell("B6"); B6.getFont().setBold(true); B6.setValue("飞机票价"); Cell B9 = wb.openSheet("Sheet1").openCell("B9"); B9.getFont().setBold(true); B9.setValue("酒店"); Cell B11 = wb.openSheet("Sheet1").openCell("B11"); B11.getFont().setBold(true); B11.setValue("餐饮"); Cell B12 = wb.openSheet("Sheet1").openCell("B12"); B12.getFont().setBold(true); B12.setValue("交通费用"); Cell B13 = wb.openSheet("Sheet1").openCell("B13"); B13.getFont().setBold(true); B13.setValue("休闲娱乐"); Cell B14 = wb.openSheet("Sheet1").openCell("B14"); B14.getFont().setBold(true); B14.setValue("礼品"); Cell B15 = wb.openSheet("Sheet1").openCell("B15"); B15.getFont().setBold(true); B15.getFont().setSize(10); B15.setValue("其余费用"); wb.openSheet("Sheet1").openCell("C6").setValue("机票单价(往)"); wb.openSheet("Sheet1").openCell("C7").setValue("机票单价(返)"); wb.openSheet("Sheet1").openCell("C8").setValue("其余"); wb.openSheet("Sheet1").openCell("C9").setValue("每晚费用"); wb.openSheet("Sheet1").openCell("C10").setValue("其余"); wb.openSheet("Sheet1").openCell("C11").setValue("天天费用"); wb.openSheet("Sheet1").openCell("C12").setValue("天天费用"); wb.openSheet("Sheet1").openCell("C13").setValue("总计"); wb.openSheet("Sheet1").openCell("C14").setValue("总计"); wb.openSheet("Sheet1").openCell("C15").setValue("总计"); wb.openSheet("Sheet1").openCell("G6").setValue(" 张"); wb.openSheet("Sheet1").openCell("G7").setValue(" 张"); wb.openSheet("Sheet1").openCell("G9").setValue(" 晚"); wb.openSheet("Sheet1").openCell("G10").setValue(" 晚"); wb.openSheet("Sheet1").openCell("G11").setValue(" 天"); wb.openSheet("Sheet1").openCell("G12").setValue(" 天"); //设置单元格包含的公式 wb.openSheet("Sheet1").openCell("H6").setFormula("=D6*F6"); wb.openSheet("Sheet1").openCell("H7").setFormula("=D7*F7"); wb.openSheet("Sheet1").openCell("H8").setFormula("=D8*F8"); wb.openSheet("Sheet1").openCell("H9").setFormula("=D9*F9"); wb.openSheet("Sheet1").openCell("H10").setFormula("=D10*F10"); wb.openSheet("Sheet1").openCell("H11").setFormula("=D11*F11"); wb.openSheet("Sheet1").openCell("H12").setFormula("=D12*F12"); wb.openSheet("Sheet1").openCell("H13").setFormula("=D13*F13"); wb.openSheet("Sheet1").openCell("H14").setFormula("=D14*F14"); wb.openSheet("Sheet1").openCell("H15").setFormula("=D15*F15"); for (int i = 0; i < 10; i++) { //设置数据以货币形式显示 wb.openSheet("Sheet1").openCell("D" + (6 + i)).setNumberFormatLocal("¥#,##0.00;¥-#,##0.00"); wb.openSheet("Sheet1").openCell("H" + (6 + i)).setNumberFormatLocal("¥#,##0.00;¥-#,##0.00"); } Cell E16 = wb.openSheet("Sheet1").openCell("E16"); E16.getFont().setBold(true); E16.getFont().setSize(11); E16.setForeColor(Color.white); E16.setValue("出差开支总费用"); E16.setVerticalAlignment(XlVAlign.xlVAlignCenter); Cell E17 = wb.openSheet("Sheet1").openCell("E17"); E17.getFont().setBold(true); E17.getFont().setSize(11); E17.setForeColor(Color.white); E17.setFormula("=IF(C4>H16,\"低于预算\",\"超出预算\")"); E17.setVerticalAlignment(XlVAlign.xlVAlignCenter); Cell H16 = wb.openSheet("Sheet1").openCell("H16"); H16.setVerticalAlignment(XlVAlign.xlVAlignCenter); H16.setNumberFormatLocal("¥#,##0.00;¥-#,##0.00"); H16.getFont().setName("Arial"); H16.getFont().setSize(11); H16.getFont().setBold(true); H16.setFormula("=SUM(H6:H15)"); Cell H17 = wb.openSheet("Sheet1").openCell("H17"); H17.setVerticalAlignment(XlVAlign.xlVAlignCenter); H17.setNumberFormatLocal("¥#,##0.00;¥-#,##0.00"); H17.getFont().setName("Arial"); H17.getFont().setSize(11); H17.getFont().setBold(true); H17.setFormula("=(C4-H16)"); // 填充数据 Cell C4 = wb.openSheet("Sheet1").openCell("C4"); C4.setNumberFormatLocal("¥#,##0.00;¥-#,##0.00"); C4.setValue("2500"); Cell D6 = wb.openSheet("Sheet1").openCell("D6"); D6.setNumberFormatLocal("¥#,##0.00;¥-#,##0.00"); D6.setValue("1200"); wb.openSheet("Sheet1").openCell("F6").getFont().setSize(10); wb.openSheet("Sheet1").openCell("F6").setValue("1"); Cell D7 = wb.openSheet("Sheet1").openCell("D7"); D7.setNumberFormatLocal("¥#,##0.00;¥-#,##0.00"); D7.setValue("875"); wb.openSheet("Sheet1").openCell("F7").setValue("1"); //打开文件 PageOfficeCtrl poCtrl1 = new PageOfficeCtrl(request); poCtrl1.setWriter(wb); poCtrl1.setServerPage("poserver.do"); poCtrl1.webOpen("doc/test.xls", OpenModeType.xlsNormalEdit, "");
可能有人会说上面的代码有点多,编程工做量大,可是当你对比一下方案一中POI生成的那个表格和这个demo中生成的表格用多大的差异,就不会有这种感受。上面的代码生成的文件效果以下图所示:
就生成这个预算表来讲,换用其余的任何组件来实现(包括开源和非开源的,固然也包括POI)都很难想象要写多少行代码。做为任何一个系统的开发人员工做的重点都应该放在具体的业务逻辑上,把大量的精力和编码工做放在用程序实现表格的绘制实际上是很不明智,这个demo也只是展现了一下PageOffice是能够这样作的,但还有更好的、任何开源组件都没有或处理很差的方法,那就是使用现有的Excel文件模板。从上面的例子能够看出PageOffice 给excel单元格赋值,也能够赋值公式,因此最好的办法就是对用户现有的excel模板编程,直接对单元格赋值数据和公式,代码量会急速的减小,一样是生成上面的一个预算表,若是模板是下面这样的:
那么编写的代码就只剩下填充数据的代码了:
Workbook wb = new Workbook(); //填充单元格文本和公式 B6.setValue("飞机票价"); Cell B9 = wb.openSheet("Sheet1").openCell("B9"); B9.getFont().setBold(true); B9.setValue("酒店"); Cell B11 = wb.openSheet("Sheet1").openCell("B11"); B11.getFont().setBold(true); B11.setValue("餐饮"); Cell B12 = wb.openSheet("Sheet1").openCell("B12"); B12.getFont().setBold(true); B12.setValue("交通费用"); Cell B13 = wb.openSheet("Sheet1").openCell("B13"); B13.getFont().setBold(true); B13.setValue("休闲娱乐"); Cell B14 = wb.openSheet("Sheet1").openCell("B14"); B14.getFont().setBold(true); B14.setValue("礼品"); Cell B15 = wb.openSheet("Sheet1").openCell("B15"); B15.getFont().setBold(true); B15.getFont().setSize(10); B15.setValue("其余费用"); wb.openSheet("Sheet1").openCell("C6").setValue("机票单价(往)"); wb.openSheet("Sheet1").openCell("C7").setValue("机票单价(返)"); wb.openSheet("Sheet1").openCell("C8").setValue("其余"); wb.openSheet("Sheet1").openCell("C9").setValue("每晚费用"); wb.openSheet("Sheet1").openCell("C10").setValue("其余"); wb.openSheet("Sheet1").openCell("C11").setValue("天天费用"); wb.openSheet("Sheet1").openCell("C12").setValue("天天费用"); wb.openSheet("Sheet1").openCell("C13").setValue("总计"); wb.openSheet("Sheet1").openCell("C14").setValue("总计"); wb.openSheet("Sheet1").openCell("C15").setValue("总计"); wb.openSheet("Sheet1").openCell("G6").setValue(" 张"); wb.openSheet("Sheet1").openCell("G7").setValue(" 张"); wb.openSheet("Sheet1").openCell("G9").setValue(" 晚"); wb.openSheet("Sheet1").openCell("G10").setValue(" 晚"); wb.openSheet("Sheet1").openCell("G11").setValue(" 天"); wb.openSheet("Sheet1").openCell("G12").setValue(" 天"); // 填充数据 Cell C4 = wb.openSheet("Sheet1").openCell("C4"); C4.setNumberFormatLocal("¥#,##0.00;¥-#,##0.00"); C4.setValue("2500"); Cell D6 = wb.openSheet("Sheet1").openCell("D6"); D6.setNumberFormatLocal("¥#,##0.00;¥-#,##0.00"); D6.setValue("1200"); wb.openSheet("Sheet1").openCell("F6").getFont().setSize(10); wb.openSheet("Sheet1").openCell("F6").setValue("1"); Cell D7 = wb.openSheet("Sheet1").openCell("D7"); D7.setNumberFormatLocal("¥#,##0.00;¥-#,##0.00"); D7.setValue("875"); wb.openSheet("Sheet1").openCell("F7").setValue("1");
这就变成了一个简单的工做量的问题,不须要绞尽脑汁去计算怎么绘制表格才更合理,才能实现效果。
就视觉效果来讲,PageOffice处理生成的表格,不论是表格边框的处理,仍是单元格背景色,或者字体和对齐方式等等都处理的很是好,尤为对Excel公式的支持更是可圈可点,就这个功能,通常用户想要的结构比较复杂的报表均可以实现。
从性能上看,文件生成的速度飞快,几乎是瞬间完成,还同时实现了文件在线打开、是否只读和打印的控制。
两种方案的选择:对于客户端数量小的系统,若是也不须要文件的在线的打开查看或编辑打印(PageOffice生成文件时能够有界面,也能够没有界面,不要被我上面说的那个例子误导),那么可使用POI。当可能会出现大量客户端并发访问或同时处理多个大文件时, POI须要大量占用服务器内存,因为Word文档固有的复杂度,POI会大量占用CPU时间,写入时长时间占用磁盘文件,会致使网页反应速度降低、阻塞现象的发生,仍是选择使用PageOffice。毕竟PageOffice是商业软件是收费的,根据具体的项目需求选择不一样的方案更实际。