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>";
} |
上面這段代碼一次取出各行各列的內容,并放入字符串,提供給后面進行輸出。