Java对Excel表格的操作
目前软件开发多采用B/S架构。正B/S架构可以给软件的开发部署带来很大的便利,但是与此同时这种架构也带来了一些问题,例如Web报表的处理等,在B/S架构下,客户端的开发并没有丰富的、互动性能良好的界面组件供开发人员调用,而且调用客户端机器上的设备也很不方便。这些缺陷将会导致B/S架构的软件在客户端的一些功能开发陷入困境。
Web报表的开发是一个比较常见的功能,然而在B/S架构上实现这些功能并没有在C/S架构上那么简单,针对这样的问题,在下面的内容中将介绍JSP与Excel的交互、图形报表的制作和基本的Web打印功能,这些功能都不是很难,可以在掌握这些功能的基础上举一反三,思考其他新的应用。
1JSP对Excel报表的处理
在应用系统开发的过程中,很多客户会提出把数据表格导出为Excel文件的需求,这样就可以利用Excel的强大功能做一些统计计算。Java自带的API中并没直接操作Excel文档的方法,如果要在Java中处理Excel文档只有借助于第三方的解决方案。在接下来的章节中将要介绍的就是利用这些第三方的类库处理Excel文档的具体方法。
1.1JSP操作Excel工具汇总
在Java处理Excel这个领域已经有很多开源的解决方案,目前在这方面做得比较出色的有ApachePOI和JExcelApi(jxl)。
ApachePOI是Apache基金组织Jakarta项目的子项目。POI包括一系列的API,可以操作多种格式的Microsoft Office文件,通过这些API可以在Java中很方便地读写Excel、Word等文件。POI是比较完整的Java Excel和Java Word解决方案。其子项目包括:POIFS、HSSF、HDF、HPSF。其中HSSF是Java到Microsoft Excel97/2002文件的接口,支持读写功能。
JExcelApi也是一个Java操作Excel的接口。它也是一个开源的解决方案,虽然在名气方面比不上大名鼎鼎的ApachePOI,但是在操作Excel的功能上丝毫不比POI逊色,而且在某些方面做得比POI更出色,例如生成Excel文件时给合并单元格加边框的问题,在POI中这很难实现的,POI的官方仅仅承诺在以后的版本中会添加这个功能。然而在JExceApi中通过一个简单的设置语句就可以实现。而且总体上JExcelApi使用都是比较简单方便的。
利用Java进行开发,尤其当使用第三方类的库进行开发的时候,最让人头疼的就是中文乱码问题,在这方面就连Apache POI也不例外,在生成Excel文件时必须经过复杂的编码设置才能看到中文显示。但是使用JExcelApi就没有这个问题,只需要简单的选择即可生成漂亮的中文Excel文件,这也是很多开发人员愿意选择这个API的又一个重要原因。
采用这两种工具都可以很方便地操作Excel文件,在这里只介绍JExcelApi的使用方法,其他类似的第三方类库在使用方法上都很类似,参考其文档都是很容易学习的。
1.2JExcelAPi开发环境简单配置
JExcelAPi是一个开源的项目,可以在官方网站下载其最新版本。在JExcelAPi的官方网站上提供各种版本的下载,例如要下载版本为2.4.2的JExcelApi,下载下来的文件为:
Jexcelapi-2-4-2.tar.gz,直接解压这个压轴文件即可。
其中docs目录下是类库参考档案。Src目录下是整个JExcelAPi的源代码,在src目录下有demo子目录,里面是例子代码,demo中的源代码对初学者来说是最好的教材,参考其中的例程可以实现其绝大部分功能。
JExcelAPi这个目录下面可以看到jx1.jar文件,这个文件就是JExcelAPi打包的类库文件,如果要在项目中使用JExcelAPi只需要把jx1.jar文件的路径加入classpath中或项目lib目录下。
1.3JSP生成Excel报表
在接下讲解在JSP中使用JExcelApi生成不同格式的Excel文件.在WEB应用开发过程中,可能会遇到各种各样的报表需求,这些报表不仅布局格式复杂,而且数据类型也是多种多样,甚至有些报表需要在指定的位置显示图片。当这些报表需要导出为Excel的时候,相应的的问题就会出现,而接下来要阐述的内容就是怎样使用JExcelApi来解决这些问题。解决任何问题的时候都是从简单到复杂,下面几个示例也是按照这个原则组织的。
在实际应用开发中,经常需要把指定的数据生成Excel文件,并且可以下载生成的Excel文件。在本章的示例中。利用JavaBean生成的Excel文件,在JSP页面上调用这个JavaBean生成的Excel文件,然后提供下载方式。当访问这个JSP页面的时候可以直接下载生成的Excel文件。
1. JSP生成简单的Excel文件
假设下面这种情形,要把表中的内容导出为Excel文件。
学校 |
专业 |
专业竞争力 |
清华大学 |
计算机专业 |
高 |
北京大学 |
法律专业 |
中 |
北京理工大学 |
航空专业 |
低 |
在表中展示的内容格式全是字符串。而且这个表格的格式也是相当简单,没有任何合并的单元格,也没有颜色的设置。类似这种表格生成对应的Excel文件是非常容易的,实现这个功能的JavaBean代码如下所示。
package beans.excel;
import java.io.IOException;
import java.io.OutputStream;
import jxl.Workbook;
import jxl.write.Label;
import jxl.write.WritableCellFormat;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import jxl.write.biff.RowsExceededException;
public class SimpleExcelWrite {
public void createExcel(OutputStream os) throws WriteException, IOException
{
//创建工作薄
WritableWorkbook workbook = Workbook.createWorkbook(os);
//创建新的一页
WritableSheet sheet = workbook.createSheet("First Sheet", 0);
//创建要显示的具体内容
Label xuexiao = new Label(0, 0, "学校");
sheet.addCell(xuexiao);
Label zhuanye = new Label(1, 0, "专业");
sheet.addCell(zhuanye);
Label jingzhengli = new Label(2, 0, "专业竞争力");
sheet.addCell(jingzhengli);
Label qinghua = new Label(0, 1, "清华大学");
sheet.addCell(qinghua);
Label jisuanji = new Label(1, 1, "计算机专业");
sheet.addCell(jisuanji);
Label gao = new Label(2, 1, "高");
sheet.addCell(gao);
Label beida = new Label(0, 2, "北京大学");
sheet.addCell(beida);
Label falv = new Label(1, 2, "法律专业");
sheet.addCell(falv);
Label zhong = new Label(2, 2, "中");
sheet.addCell(zhong);
Label ligong = new Label(0, 3, "北京理工大学");
sheet.addCell(ligong);
Label hangkong = new Label(1, 3, "航空专业");
sheet.addCell(hangkong);
Label di = new Label(2, 3, "低");
sheet.addCell(di);
//把创建的内容写入到输出流中,并关闭输出流
workbook.write();
workbook.close();
os.close();
}
}
|
上面这个JavaBean中的主要代码解释如下。
WritableWorkbook workbook = Workbook.createWorkbook(os); |
上面这行代码创建一个Excel工作区(WorkBook)。在Excel中,所有的页(Sheet)只能在工作区(WorkBook)中创建。
WritableSheet sheet = workbook.createSheet("First Sheet", 0); |
上面这行代码在工作区(WorkBook)中创建新的一页(Sheeet)其中新建的页(Sheet)名称为“First sheet”。这一页的属性是可以进行写操作的。在JExcelAPi中也可以创建只读的页。
Label xuexiao = new Label(0, 0, "学校");
sheet.addCell(xuexiao); |
上面这段代码创建了一个单元格,并把这个单元格添加到指定的页中,其中这个单元格的内容是“学校”位置在第一行第一列,其中第1个参数是列坐标,第2个参数是行坐标,而且两个坐标都是从0开始计算。
workbook.write();
workbook.close();
os.close();
|
这3行代码执行的操作是把工作区中的内容写到输出流中,然后关闭工作区,最后关闭输出流。
下面来看如何在JSP页面上调用这个JavaBean,并且实现下载的功能。具体的JSP代码如下所示。
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
<%@ page import="java.io.*" %>
<%@ page import="beans.excel.*" %>
<%
String fname = "学校专业竞争力情况";
OutputStream os = response.getOutputStream();//取得输出流
response.reset();//清空输出流
//下面是对中文文件名的处理
response.setCharacterEncoding("UTF-8");
fname = java.net.URLEncoder.encode(fname, "UTF-8");
response.setHeader("Content-Disposition", "attachment; filename="+ new String(fname.getBytes("UTF-8"), "GBK") + ".xls");
response.setContentType("application/msexcel");//定义输出类型
SimpleExcelWrite sw = new SimpleExcelWrite();
sw.createExcel(os);
%>
<html>
<body>
</body>
</html>
|
上面这个JSP页面实现的功能是调用JavaBean生成的Excel文件,并且提供下载。
String fname = "学校专业竞争力情况"; |
上面这行设置生成Excel文件的文件名。
OutputStream os = response.getOutputStream();//取得输出流
response.reset();//清空输出流 |
上面两行代码取得输出流,并且清空输出流的男内容。提供给后面生成的Excel文件使用。
response.setCharacterEncoding("UTF-8");
fname = java.net.URLEncoder.encode(fname, "UTF-8"); |
上面两行代码进行的操作是中文显示的处理,上面一行设置整个Excel的编码格式,下面一行设置Excel文件名的编码格式。这两处编码格式如果不进行设置就会出现中文乱码的情况。
response.setHeader("Content-Disposition", "attachment; filename="+ new String(fname.getBytes("UTF-8"), "GBK") + ".xls");
response.setContentType("application/msexcel");//定义输出类型
|
上面这段代码实现了下载的功能。
SimpleExcelWrite sw = new SimpleExcelWrite();
sw.createExcel(os); |
上面这段代码调用SimpleExcelWrite这个JavaBean生成Excel文件,这里之所以没有使用〈jsp:useBean〉标签是因为在这个JavaBean中并没有需要设置的属性和用来获取的属性,所以用调用一般类的方法也是可以的,同样可以调用到JavaBean中的方法。
2.JSP生成各种复杂数据格式的Excel文件
在上面的示例程序中,数据格式是非常简单的,仅仅只有字符串这一种格式,下面考虑这样的情形,现在需要把表11.2中的数据导出为Excel文件
JExcelApi支持数据格式列表
数据格式 |
浮点型 |
整型 |
布尔型 |
日期格式 |
数据示例 |
3.1415926535 |
15042699 |
true |
2007-7-15 |
实现这个操作比前一个例子中的要稍微麻烦一些,需要对各种数据类型进行单独的设置,不同的数据类型需要用不同的单元格的构造方式。生成这个Excel的JavaBean具体代码如下所示。
package beans.excel;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import jxl.Workbook;
import jxl.format.Colour;
import jxl.write.DateFormats;
import jxl.write.Label;
import jxl.write.NumberFormats;
import jxl.write.WritableCellFormat;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import jxl.write.Number;
import jxl.write.Boolean;
import jxl.write.DateTime;
public class ComplexDataExcelWrite {
public void createExcel(OutputStream os) throws WriteException, IOException {
//创建工作薄
WritableWorkbook workbook = Workbook.createWorkbook(os);
//创建新的一页
WritableSheet sheet = workbook.createSheet("First Sheet", 0);
//创建要显示的具体内容
Label formate = new Label(0, 0, "数据格式");
sheet.addCell(formate);
Label floats = new Label(1, 0, "浮点型");
sheet.addCell(floats);
Label integers = new Label(2, 0, "整型");
sheet.addCell(integers);
Label booleans = new Label(3, 0, "布尔型");
sheet.addCell(booleans);
Label dates = new Label(4, 0, "日期格式");
sheet.addCell(dates);
Label example = new Label(0, 1, "数据示例");
sheet.addCell(example);
//浮点数据
Number number = new Number(1, 1, 3.1415926535);
sheet.addCell(number);
//整型数据
Number ints = new Number(2, 1, 15042699);
sheet.addCell(ints);
//布尔型数据
Boolean bools = new Boolean(3, 1, true);
sheet.addCell(bools);
//日期型数据
Calendar c = Calendar.getInstance();
Date date = c.getTime();
WritableCellFormat cf1 = new WritableCellFormat(DateFormats.FORMAT1);
DateTime dt = new DateTime(4, 1, date, cf1);
sheet.addCell(dt);
//把创建的内容写入到输出流中,并关闭输出流
workbook.write();
workbook.close();
os.close();
}
}
|
这个程序的思路和前一个例子基本一样,不同之处在于各种数据类型单元格的处理,这个在程序的注释中已经写得很清楚了。具体不同数据类型的处理请参考上面的程序代码。
这个JavaBean的调用方法和上一个例子一样,只需要稍微改动一下上一个例子中的JSP文件(SimpleExcelWrite.jsp)即可,所需改动的仅仅有下面两个地方:
String fname = "学校专业竞争力情况"; |
把上面这句中的“学校专业竞争力情况”改为“JExcelApi支持数据格式列表”。
SimpleExcelWrite sw = new SimpleExcelWrite(); |
把上面这行代码改为ComplexDataExcelWrite sw=newComplexDataExcelWrite()即可。经过这样的改动,就可以调用上面这个JavaBean。
3.JSP生成复杂布局和样式的Excel文件
上面的示例程序只是展示了JExcelApi支持的各种数据类型,接下来将要展示JExcelApi对复杂的布局和样式的支持。假设要把表中的数据导出为Excel文件
JExcelApi支持数据类型详细说明
JExcelApi支持数据类型详细说明 |
数据格式 |
浮点型 |
整型 |
布尔型 |
日期格式 |
数据示例 |
3.1415926535 |
15042699 |
true |
2007-7-15 |
在表中,表格的布局发生了变化,表头占了5列,高度也明显大于其他各行,同时各单元格的样式也有了变化,采用了不同的字体类型、背景颜色。针对这样的表格,在生成Excel文件的时候就要设置其布局和显示的属性。下面就是这个JavaBean的具体实现代码。
package beans.excel;
import java.io.IOException;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import jxl.Workbook;
import jxl.format.Colour;
import jxl.format.UnderlineStyle;
import jxl.write.DateFormats;
import jxl.write.Label;
import jxl.write.NumberFormats;
import jxl.write.WritableCellFormat;
import jxl.write.WritableFont;
import jxl.write.WritableSheet;
import jxl.write.WritableWorkbook;
import jxl.write.WriteException;
import jxl.write.Number;
import jxl.write.Boolean;
import jxl.write.DateTime;
public class MutiStyleExcelWrite {
public void createExcel(OutputStream os) throws WriteException, IOException {
//创建工作薄
WritableWorkbook workbook = Workbook.createWorkbook(os);
//创建新的一页
WritableSheet sheet = workbook.createSheet("First Sheet", 0);
//构造表头
sheet.mergeCells(0, 0, 4, 0);//添加合并单元格
WritableFont bold = new WritableFont(WritableFont.ARIAL, 10, WritableFont.BOLD);//设置字体种类和黑体显示
WritableCellFormat titleFormate = new WritableCellFormat (bold);
titleFormate.setAlignment(jxl.format.Alignment.CENTRE);//单元格中的内容水平方向居中
titleFormate.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);//单元格中的内容垂直方向居中
Label title = new Label(0,0,"JExcelApi支持数据类型详细说明",titleFormate);
sheet.setRowView(0,600,false);//设置第一行的高度
sheet.addCell(title);
//创建要显示的具体内容
WritableFont color = new WritableFont(WritableFont.ARIAL);//选择字体
color.setColour(Colour.GOLD);//设置字体颜色为金黄色
WritableCellFormat colorFormat = new WritableCellFormat(color);
Label formate = new Label(0, 1, "数据格式",colorFormat);
sheet.addCell(formate);
Label floats = new Label(1, 1, "浮点型");
sheet.addCell(floats);
Label integers = new Label(2, 1, "整型");
sheet.addCell(integers);
Label booleans = new Label(3, 1, "布尔型");
sheet.addCell(booleans);
Label dates = new Label(4, 1, "日期格式");
sheet.addCell(dates);
Label example = new Label(0, 2, "数据示例",colorFormat);
sheet.addCell(example);
//浮点数据
WritableFont underline = new WritableFont(WritableFont.ARIAL, WritableFont.DEFAULT_POINT_SIZE,
WritableFont.NO_BOLD,false,UnderlineStyle.SINGLE);//设置下划线
WritableCellFormat greyBackground = new WritableCellFormat(underline);
greyBackground.setBackground(Colour.GRAY_25);//设置背景颜色为灰色
Number number = new Number(1, 2, 3.1415926535,greyBackground);
sheet.addCell(number);
//整型数据
WritableFont boldNumber = new WritableFont(WritableFont.ARIAL, 10, WritableFont.BOLD);//设置黑体
WritableCellFormat boldNumberFomate = new WritableCellFormat (boldNumber);
Number ints = new Number(2, 2, 15042699,boldNumberFomate);
sheet.addCell(ints);
//布尔型数据
Boolean bools = new Boolean(3, 2, true);
sheet.addCell(bools);
//日期型数据
WritableFont boldDate = new WritableFont(WritableFont.ARIAL, WritableFont.DEFAULT_POINT_SIZE,
WritableFont.BOLD,false,UnderlineStyle.SINGLE);//设置黑体和下划线
WritableCellFormat boldDateFomate = new WritableCellFormat (boldDate,DateFormats.FORMAT1);
Calendar c = Calendar.getInstance();
Date date = c.getTime();
DateTime dt = new DateTime(4, 2, date, boldDateFomate);
sheet.addCell(dt);
//把创建的内容写入到输出流中,并关闭输出流
workbook.write();
workbook.close();
os.close();
}
}
|
从上面的程序中可以看出JExcelApi设置格式和样式的思路,如果需要天加合并单元格只需要在Sheet 上面添加mergeCells即可。而对单元格的样式进行设置的时候,只需要构造一个 WritableCellFormat即可,在这个对象里面可以进行除字体外的各种样式设置,如果需要设置字体,在构造WritableCellFormat前就需要构造 WritableFont,然后把字体设置完成的WritableFont对象作为参数传给WritableCellFormat即可。
下面来解释这个Java Bean的关键代码。
sheet.mergeCells(0, 0, 4, 0);//添加合并单元格 |
上面这行代码向页(Sheet)里面添加一个合并单元格,这个合并单元格的区域是第一行到第五列。其中第一个参数是起始行,第三个参数是终止列,第四个参数是终止行。
WritableFont bold = new WritableFont(WritableFont.ARIAL, 10, WritableFont.BOLD); |
上面这行代码构造了一个字体对象,其中选择的字体为ARIAL,字号大小为10,用黑体显示。
WritableCellFormat titleFormate = new WritableCellFormat (bold); |
上面这行代码利用前面构造的字体对象生成一个单元格样式控制对象。
titleFormate.setAlignment(jxl.format.Alignment.CENTRE);//单元格中的内容水平方向居中
titleFormate.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);//单元格中的内容垂直方向居中
|
单元格样式控制对象创建以后,就可以在这个对象上进行各种样式设置。上面的两行代码就是在前面创建的单元格样式控制对象上进行样式设置。
Label title = new Label(0,0,"JExcelApi支持数据类型详细说明",titleFormate); |
在样式结束后,只需要在构造单元格的时候选择这个单元格样式控制对象即可。上面这行代码中的titleFormate就是前面已经构造并设置样式的单元格样式控制对象。
这个程序中其他样式的设置思路和这个基本相同,只是选择的具体参数不同。读者可以仔细揣摩。
上面这个Java Bean的调用方法和前面两个Excel文件生成示例的调用方法相同,只需要把SimpleExcelWrite.jsp这个JSP页面做如下修改即可。
String fname = "学校专业竞争力情况"; |
把上面这句中的“学校专业竞争力情况”改为“JExcelApi支持数据类型详细说明”
SimpleExcelWrite sw = new SimpleExcelWrite(); |
把上面这行代码改为MutiStyleExcelWrite sw=newMutiStyleExcelWrite()即可。
4.JSP生成带有图片的Excel文件
在JExcelApi中,生成带有图片的Excel文件非常方便,只需构造一个图片单元格即可。具体构造代码如下所示。
WritableImage wi = new WritableImage(0,0,5,0,new File(“resource/123.jpg")); |
上面这段代码构造了一个图片单元格,其所占区域为第一行的第一列到第五列,这里第一个参数为起始列,第二个参数为起始行,第三个参数为终止列,第四个参数为终止行,第五个参数是所要显示图片的文件对象,其中“resource/123.jpg”是文件的目录。
添加图象单元格的过程和普通的单元格没有什么区别,读者可以把上面这段代码加入前面的例子中,正确指定显示区域和图片文件的路径即可显示。
11.1.4JSP读取Excel报表、
这样对所有的单元格都可以使用同一处理方法进行处理。针对读取Excel的操作没有很多内容,在这里把逻辑代码放在JSP页面进行处理,具体的处理过程可以参考下面的代码。
<%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
<%@ page import="java.io.File" %>
<%@ page import="jxl.Cell" %>
<%@ page import="jxl.Sheet" %>
<%@ page import="jxl.Workbook" %>
<html>
<body>
<font size="2">
<%
String fileName = "E:/Devolop Tool/Tomcat 5.0/webapps/chapt11/学校专业竞争力情况.xls";
File file = new File(fileName);
Workbook wb = Workbook.getWorkbook(file);
Sheet sheet = wb.getSheet(0);
String outPut = "";
outPut = outPut + "<b>" + fileName + "</b><br>";
outPut = outPut + "第一个sheet的名称为:" + sheet.getName() + "<br>";
outPut = outPut + "第一个sheet共有:"+sheet.getRows()+"行"+sheet.getColumns()+"列<br>";
outPut = outPut + "具体内容如下:<br>";
for (int i = 0; i < sheet.getRows(); i++) {
for (int j = 0; j < sheet.getColumns(); j++) {
Cell cell = sheet.getCell(j, i);
outPut = outPut + cell.getContents()+" ";
}
outPut = outPut +"<br>";
}
out.println(outPut);
%>
</font>
</body>
</html> |
下面来解释这段程序的关键代码.
File file = new File(fileName); |
上面这行代码就是一个简单的文件操作,根据文件名创建一个文件对象.
Workbook wb = Workbook.getWorkbook(file); |
上面这行代码从文件流中取得Excel工作区对象(WorkBook)。
Sheet sheet = wb.getSheet(0); |
上面这行代码从工作区中取得页(Sheet),取得这个对象的时候既可以用名称来获得,也可以用序号,在这里我们采用序号,取得第一页。
outPut = outPut + "第一个sheet的名称为:" + sheet.getName() + "<br>"; |
上面这行代码取出页的名称,并放入字符串,提供给后面进行输出。
outPut = outPut + "第一个sheet共有:"+sheet.getRows()+"行"+sheet.getColumns()+"列<br>"; |
上面这行代码取出页的行数和列数,并放入字符串,提供给后面进行输出。
for (int i = 0; i < sheet.getRows(); i++) {
for (int j = 0; j < sheet.getColumns(); j++) {
Cell cell = sheet.getCell(j, i);
outPut = outPut + cell.getContents()+" ";
}
outPut = outPut +"<br>";
} |
上面这段代码一次取出各行各列的内容,并放入字符串,提供给后面进行输出。