1 開發調研java
1.1 需求描述 MS的電子表格(Excel)是Office的重要成員,是保存統計數據的一種經常使用格式。做爲辦公文檔,勢必要涉及到的電子文檔的交換,Excel是一種在企業中很是通用的文件格式,打印和管理也比較方便。在一個Java應用中,將一部分數據生成Excel格式,是與其餘系統無縫鏈接的重要手段。 1.2 Excel開發經常使用開源工具 在開源世界中,有兩套比較有影響的API可供使用,一個是POI,一個是jExcelAPI。 1.3 比較開源工具的優缺點 1.3.1 Jxl優缺點 Jxl特徵有以下描述: ● 支持Excel 95-2000的全部版本 ● 生成Excel 2000標準格式 ● 支持字體、數字、日期操做 ● 可以修飾單元格屬性 ● 支持圖像和圖表 應該說以上功能已經可以大體知足咱們的須要。最關鍵的是這套API是純Java的,並不依賴Windows系統,即便運行在Linux下,它一樣可以正確的處理Excel文件。另外須要說明的是,這套API對圖形和圖表的支持頗有限,並且僅僅識別PNG格式。 1.3.2 Poi優缺點 Jakarta 的 POI Project 與 Java Excel API 在開源世界中能夠說是並駕齊驅,可是也各有優劣,poi在某些細節有些小Bug而且不支持寫入圖片(poi其實能夠寫入圖片,不過沒有jxl來的方便,比較麻煩),其餘方面都挺不錯的;而JXL提供了對圖片的支持(可是僅僅支持png格式的圖片),問題就是對公式支持不是很好,但仍是提供了簡單的公式讀取支持。所以你的項目中要選用什麼樣的第三方插件爲徹底由你的應用來決定。若是你的軟件是跟財務有至關的關係的話,建議採用 POI Project,就我所在目前的項目來講因爲用不到計算公式,並且極可能須要導出圖片,所以,個人選擇是 JXL 。web
1.4 性能比較以及最終選擇 1.4.1 內存消耗:(來自網絡) 談下JVM虛擬機內存消耗的狀況. 數據量3000條數據,每條60列.JVM虛擬機內存大小64M. 使用POI:運行到2800條左右就報內存溢出. 使用JXL:3000條所有出來,而且內存還有21M的空間. 可想而知,在對內存的消耗方面差距仍是挺大的. 也許是因爲JXL在對資源回收利用方面作的還挺不錯的。 1.4.2 速度效率(讀取excel數據)(來自網絡) 文件 POI加載耗時 POI總耗時 JXL加載耗時 Jxl總耗時 文件大小57KB 1172 ms 1172 ms 1265 ms 2250 ms 文件大小652KB 2297 ms 2313 ms 4406 ms 9750 ms 文件大小2.24M 3109ms 3140ms 16313ms 37453msapi
1.4.3 寫excel速度效率 jxl插入數據比poi速度要快 1.4.4 功能對比 相比提供的功能的話,JXL相對弱了點.因此若是要實現的功能比較複雜的狀況下能夠考慮使用POI,但若是隻想生成一些大數據量能夠考慮使用JXL,或者CSV也是一個不錯的選擇,不過CSV並非真正的excel,然而jxl插入數據比poi速度要快。數組
2 Jxl開發指南 2.1 介紹 jxl操做excel包括對象Workbook,Sheet ,Cell。 一個excel就對應一個Workbook對象, 一個Workbook能夠有多個Sheet對象 一個Sheet對象能夠有多個Cell對象 2.2 讀取excel操做 經過Workbook,Sheet ,Cell這三個對象咱們就能夠實現Excel文件的讀取工做。咱們先想一想一下讀取步驟,不論是什麼樣的Excel操做框架一定都要經歷 一、 選取Excel文件獲得工做薄 二、 選擇工做表 三、 選擇Cell 四、 讀取信息 2.2.1 讀取工做薄 選取Excel文件獲得工做薄Workbook瀏覽器
Workbook workbook = Workbook.getWorkbook(new File("myfile.xls"));
2.2.2 讀取工做表 經過Workbook的getSheet方法選擇第一個工做表(從0開始)服務器
Sheet sheet = workbook.getSheet(0);
也能夠經過工做的名稱來獲得Sheet 2.2.3 讀取單元格 經過Sheet的getCell方法選擇位置爲C2的單元格(兩個參數都從0開始)網絡
Cell c2 = sheet.getCell(2,1);
2.2.3.1 讀取單元格的值 2.2.3.2 經過Cell的getContents方法 把單元格中的信息以字符的形式讀取出來String stringc2 = c2.getContents(); 2.2.3.3 Cell提供了一個getType方法 可以返回單元格的類型信息,同時JXL提供了一個CellType類用來預設Excel中的類型信息,並且JXL提供了一些Cell類的子類用來分別用來表示各類類型的單元格,如LabelCell,NumberCell,DateCell分別表示字符、數值、日期類型的單元格框架
if (c2.getType() == CellType. LABEL) { LabelCell nc = (LabelCell) c2; String number b2 = nc. getString(); } if (c2.getType() == CellType. DATE) { DateCell nc = (DateCell) c2; Date number b2 = nc. getDate(); } if (c2.getType() == CellType.NUMBER) { NumberCell nc = (NumberCell) c2; double number b2 = nc.getValue(); }
API提供瞭如下基本類型,與Excel的數據格式相對應,以下圖所示工具
2.2.4 以釋放資源:workbook.close() 當你完成對Excel電子表格數據的處理後,必定要使用close()方法來關閉先前建立的對象,以釋放讀取數據表的過程當中所佔用的內存空間,在讀取大量數據時顯得尤其重要 最後不要忘記關閉workbook以釋放資源:workbook.close(); 2.3 寫excel操做 經過WritableWorkbook,WritableSheet,Label這三個對象咱們就能夠實現Excel文件的插入工做。咱們先想一想一下插入,不論是什麼樣的Excel操做框架一定都要經歷 一、 建立Exce工做薄 二、 建立工做表 三、 建立單元格性能
2.3.1 建立工做薄 API提供了兩種方式來處理可寫入的輸出流,一種是直接生成本地文件,若是文件名不帶全路徑的話,缺省的文件會定位在當前目錄,若是文件名帶有全路徑的話,則生成的Excel文件則會定位在相應的目錄;另一種是將Excel對象直接寫入到輸出流,例如:用戶經過瀏覽器來訪問web服務器,若是HTTP頭設置正確的話,瀏覽器自動調用客戶端的Excel應用程序,來顯示動態生成的Excel電子表格。 2.3.1.1 建立可寫入的Excel工做薄 WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile)); 2.3.1.2 將WritableWorkbook直接寫入到輸出流 OutputStream os = new FileOutputStream(targetfile); WritableWorkbook wwb = Workbook.createWorkbook(os); 2.3.2 建立工做表 WritableSheet ws = wwb.createSheet("通信錄", 0);//建立sheet 2.3.3 建立單元格 2.3.3.1 添加文本類單元格 Label labelC = new Label(0, 0, "This is a Label cell"); 2.3.3.2 添加帶有字型Formatting的對象 WritableFont wf = new WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true); WritableCellFormat wcfF = new WritableCellFormat(wf); labelCF = new Label(1, 0, "This is a Label Cell", wcfF); ws.addCell(labelCF); 2.3.3.3 添加帶有字體顏色Formatting的對象 WritableFont wfc = new WritableFont(WritableFont.ARIAL,10,WritableFont.NO_BOLD, false, UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.RED); WritableCellFormat wcfFC = new WritableCellFormat(wfc); Label labelCFC = new Label(1, 0, "This is a Label Cell", wcfFC); ws.addCell(labelCF); 2.3.3.4 添加Number對象 Number labelN = new jxl.write.Number(0, 1, 3.1415926); ws.addCell(labelN); 2.3.3.5 添加帶有formatting的Number對象 NumberFormat nf = new NumberFormat("#.##"); WritableCellFormat wcfN = new WritableCellFormat(nf); Number labelNF = new Number(1, 1, 3.1415926, wcfN); ws.addCell(labelNF); 2.3.3.6 添加Boolean對象 Boolean labelB = new jxl.write.Boolean(0, 2, false); ws.addCell(labelB); 2.3.3.7 添加DateTime對象 DateTime labelDT = new DateTime(0, 3, new java.util.Date()); ws.addCell(labelDT); 2.3.3.8 添加帶有formatting的DateFormat對象 DateFormat df = new DateFormat("dd MM yyyy hh:mm:ss"); WritableCellFormat wcfDF = new WritableCellFormat(df); DateTime labelDTF = new DateTime(1, 3, new Date(), wcfDF); ws.addCell(labelDTF); 2.3.3.9 添加公式單元格 Fornual formual = new Formual(0,11,」Sum(A1:A9)」); wrb.addCell(formual); 2.3.3.10 添加圖像 WritableImage wrimage=new WritableImage(1,5,10,10,new File(imageFilepath)); wrb.addImage(wrimage); 注意,API中註明只支持png文件。 2.3.4 合併單元格 經過writablesheet.mergeCells(int x,int y,int m,int n);來實現的。 表示將從第x+1列,y+1行到m+1列,n+1行合併 (四個點定義了兩個座標,左上角和右下角)結果是合併了m-x+1行,n-y+1列,二者乘積就是合併的單元格數量。 sheet.mergeCells(0, 6, 3, 8); label = new Label(0, 6, "合併了12個單元格"); sheet.addCell(label);
2.3.5 添加單元格樣式 主要是改變單元格背景、字體、顏色等等。 WritableCellFormat wc = new WritableCellFormat(); wc.setAlignment(Alignment.CENTRE); // 設置居中 wc.setBorder(Border.ALL, BorderLineStyle.THIN); // 設置邊框線 wc.setBackground(jxl.format.Colour.RED); // 設置單元格的背景顏色 label = new Label(1, 5, "字體", wc); sheet.addCell(label); 2.3.6 設置單元格字體 WritableFont wfont = new WritableFont(WritableFont.createFont("楷書"), 20); WritableCellFormat font = new WritableCellFormat(wfont); label = new Label(2, 6, "楷書", font); sheet.addCell(label); 2.3.7 寫入到文件 wwb.write();// 寫入數據 wwb.close();// 關閉文件
2.4 拷貝、更新Excel工做薄 //建立只讀的Excel工做薄的對象 jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile)); //建立可寫入的Excel工做薄對象 WritableWorkbook wwb=Workbook.createWorkbook(new File(targetfile), rw); //讀取第一張工做表 jxl.write.WritableSheet ws = wwb.getSheet(0); //得到第一個單元格對象 jxl.write.WritableCell wc = ws.getWritableCell(0, 0); //判斷單元格的類型, 作出相應的轉化 if(wc.getType() == CellType.LABEL) { Label l = (Label)wc; l.setString("The value has been modified."); } //寫入Excel對象 wwb.write(); //關閉可寫入的Excel對象 wwb.close(); //關閉只讀的Excel對象 rw.close(); 爲了提升性能,在讀取工做表時,與數據相關的一些輸出信息,全部的格式信息,如:字體、顏色等等,是不被處理的,由於咱們的目的是得到行數據的值,既使沒有了修飾,也不會對行數據的值產生什麼影響。惟一的不利之處就是,在內存中會同時保存兩個一樣的工做表,這樣當工做表體積比較大時,會佔用至關大的內存,但如今好像內存的大小並非什麼關鍵因素了。 一旦得到了可寫入的工做表對象,咱們就能夠對單元格對象進行更新的操做了,在這裏咱們沒必要調用API提供的add()方法,由於單元格已經於工做表當中,因此咱們只須要調用相應的setXXX()方法,就能夠完成更新的操做了。 盡單元格原有的格式化修飾是不能去掉的,咱們仍是能夠將新的單元格修飾加上去,以使單元格的內容以不一樣的形式表現。 新生成的工做表對象是可寫入的,咱們除了更新原有的單元格外,還能夠添加新的單元格到工做表中。 最後,不要忘記調用write()方法,將更新的內容寫入到文件中,而後關閉工做薄對象,這裏有兩個工做薄對象要關閉,一個是隻讀的,另一個是可寫入的。
3 JXL讀寫excel文件的例子 (來自網絡) 3.1 實例一
import java.io.FileOutputStream; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import jxl.*; import jxl.format.Alignment; import jxl.format.Border; import jxl.format.BorderLineStyle; import jxl.format.CellFormat; import jxl.write.Boolean; import jxl.write.Label; import jxl.write.Number; import jxl.write.WritableCellFormat; import jxl.write.WritableFont; import jxl.write.WritableSheet; import jxl.write.WritableWorkbook; public class JXLExample { /** * * @author smart * */ public static void main(String[] args) { // 準備設置excel工做表的標題 String[] title = {"編號","產品名稱","產品價格","產品數量","生產日期","產地","是否出口"}; try { // 得到開始時間 long start = System.currentTimeMillis(); // 輸出的excel的路徑 String filePath = "c:\\test.xls"; // 建立Excel工做薄 WritableWorkbook wwb; // 新創建一個jxl文件,即在C盤下生成test.xls OutputStream os = new FileOutputStream(filePath); wwb=Workbook.createWorkbook(os); // 添加第一個工做表並設置第一個Sheet的名字 WritableSheet sheet = wwb.createSheet("產品清單", 0); Label label; for(int i=0;i<title.length;i++){ label = new Label(i,0,title[i]); // 將定義好的單元格添加到工做表中 sheet.addCell(label); } /* * 保存數字到單元格,須要使用jxl.write.Number * 必須使用其完整路徑,不然會出現錯誤 * */ // 填充產品編號 jxl.write.Number number = new jxl.write.Number(0,1,20071001); sheet.addCell(number); // 填充產品名稱 label = new Label(1,1,"金鴿瓜子"); sheet.addCell(label); /* * 定義對於顯示金額的公共格式 * jxl會自動實現四捨五入 * 例如 2.456會被格式化爲2.46,2.454會被格式化爲2.45 * */ jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##"); jxl.write.WritableCellFormat wcf = new jxl.write.WritableCellFormat(nf); // 填充產品價格 jxl.write.Number nb = new jxl.write.Number(2,1,2.45,wcf); sheet.addCell(nb); // 填充產品數量 jxl.write.Number numb = new jxl.write.Number(3,1,200); sheet.addCell(numb); /* * 定義顯示日期的公共格式 * 如:yyyy-MM-dd hh:mm * */ SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String newdate = sdf.format(new Date()); // 填充出產日期 label = new Label(4,1,newdate); sheet.addCell(label); // 填充產地 label = new Label(5,1,"陝西西安"); sheet.addCell(label); /* * 顯示布爾值 * */ jxl.write.Boolean bool = new jxl.write.Boolean(6,1,true); sheet.addCell(bool); /* * 合併單元格 * 經過writablesheet.mergeCells(int x,int y,int m,int n);來實現的 * 表示將從第x+1列,y+1行到m+1列,n+1行合併 * * */ sheet.mergeCells(0,3,2,3); label = new Label(0,3,"合併了三個單元格"); sheet.addCell(label); /* * * 定義公共字體格式 * 經過獲取一個字體的樣式來做爲模板 * 首先經過web.getSheet(0)得到第一個sheet * 而後取得第一個sheet的第二列,第一行也就是"產品名稱"的字體 * */ CellFormat cf = wwb.getSheet(0).getCell(1, 0).getCellFormat(); WritableCellFormat wc = new WritableCellFormat(); // 設置居中 wc.setAlignment(Alignment.CENTRE); // 設置邊框線 wc.setBorder(Border.ALL, BorderLineStyle.THIN); // 設置單元格的背景顏色 wc.setBackground(jxl.format.Colour.RED); label = new Label(1,5,"字體",wc); sheet.addCell(label); // 設置字體 WritableFont wfont=new WritableFont(WritableFont.createFont("隸書"),20); WritableCellFormat font = new WritableCellFormat(wfont); label = new Label(2,6,"隸書",font); sheet.addCell(label); // 寫入數據 wwb.write(); // 關閉文件 wwb.close(); long end = System.currentTimeMillis(); System.out.println("----完成該操做共用的時間是:"+(end-start)/1000); } catch (Exception e) { System.out.println("---出現異常---"); e.printStackTrace(); } } }
3.2 實例二
package com.test; import java.io.File; import java.io.IOException; import jxl.Cell; import jxl.Sheet; import jxl.Workbook; import jxl.format.Border; import jxl.format.BorderLineStyle; import jxl.format.Colour; import jxl.read.biff.BiffException; import jxl.write.Label; import jxl.write.WritableCellFormat; import jxl.write.WritableFont; import jxl.write.WritableSheet; import jxl.write.WritableWorkbook; import jxl.write.WriteException; import jxl.write.biff.RowsExceededException; public class JexcelSample { /** * 寫excel文件 * */ public void writeExc(File filename){ WritableWorkbook wwb = null; try { wwb = Workbook.createWorkbook(filename); } catch (Exception e){ e.printStackTrace(); } //建立Excel工做表 WritableSheet ws = wwb.createSheet("通信錄", 0);//建立sheet try { ws.mergeCells(0, 0, 2, 1);//合併單元格(左列,左行,右列,右行)從第1行第1列到第2行第3列 Label header = new Label(0, 0, "通信錄(191026班)", getHeader()); ws.addCell(header);//寫入頭 Label l = new Label(0, 2, "姓名", getTitle());//第3行 ws.addCell(l); l = new Label(1, 2, "電話", getTitle()); ws.addCell(l); l = new Label(2, 2, "地址", getTitle()); ws.addCell(l); l = new Label(0, 3, "小祝", getNormolCell());//第4行 ws.addCell(l); l = new Label(1, 3, "1314***0974", getNormolCell()); ws.addCell(l); l = new Label(2, 3, "武漢武昌", getNormolCell()); ws.addCell(l); l = new Label(0, 4, "小施", getNormolCell());//第5行 ws.addCell(l); l = new Label(1, 4, "1347***5057", getNormolCell()); ws.addCell(l); l = new Label(2, 4, "武漢武昌", getNormolCell()); ws.addCell(l); ws.setColumnView(0,20);//設置列寬 ws.setColumnView(1,20); ws.setColumnView(2,40); ws.setRowView(0,400);//設置行高 ws.setRowView(1,400); ws.setRowView(2,500); ws.setRowView(3,500); ws.setRowView(4,500); } catch (RowsExceededException e1) { e1.printStackTrace(); } catch (WriteException e1) { e1.printStackTrace(); } //輸出流 try { wwb.write(); } catch (IOException ex) { // TODO 自動生成 catch 塊 ex.printStackTrace(); } //關閉流 try { wwb.close(); } catch (WriteException ex) { // TODO 自動生成 catch 塊 ex.printStackTrace(); } catch (IOException ex) { // TODO 自動生成 catch 塊 ex.printStackTrace(); } //outStream.close(); System.out.println("寫入成功!\n"); } public void readExc(File filename) throws BiffException, IOException{ Workbook wb = Workbook.getWorkbook(filename); Sheet s = wb.getSheet(0);//第1個sheet Cell c = null; int row = s.getRows();//總行數 int col = s.getColumns();//總列數 for(int i=0;i<row;i++){ for(int j=0;j<col;j++){ c = s.getCell(j,i); System.out.print(c.getContents()+" "); } System.out.println(); } } /** * 設置頭的樣式 * @return */ public static WritableCellFormat getHeader(){ WritableFont font = new WritableFont(WritableFont.TIMES, 24 ,WritableFont.BOLD);//定義字體 try { font.setColour(Colour.BLUE);//藍色字體 } catch (WriteException e1) { // TODO 自動生成 catch 塊 e1.printStackTrace(); } WritableCellFormat format = new WritableCellFormat(font); try { format.setAlignment(jxl.format.Alignment.CENTRE);//左右居中 format.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE);//上下居中 format.setBorder(Border.ALL,BorderLineStyle.THIN,Colour.BLACK);//黑色邊框 format.setBackground(Colour.YELLOW);//黃色背景 } catch (WriteException e) { // TODO 自動生成 catch 塊 e.printStackTrace(); } return format; } /** * 設置標題樣式 * @return */ public static WritableCellFormat getTitle(){ WritableFont font = new WritableFont(WritableFont.TIMES, 14); try { font.setColour(Colour.BLUE);//藍色字體 } catch (WriteException e1) { // TODO 自動生成 catch 塊 e1.printStackTrace(); } WritableCellFormat format = new WritableCellFormat(font); try { format.setAlignment(jxl.format.Alignment.CENTRE); format.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE); format.setBorder(Border.ALL,BorderLineStyle.THIN,Colour.BLACK); } catch (WriteException e) { // TODO 自動生成 catch 塊 e.printStackTrace(); } return format; } /** * 設置其餘單元格樣式 * @return */ public static WritableCellFormat getNormolCell(){//12號字體,上下左右居中,帶黑色邊框 WritableFont font = new WritableFont(WritableFont.TIMES, 12); WritableCellFormat format = new WritableCellFormat(font); try { format.setAlignment(jxl.format.Alignment.CENTRE); format.setVerticalAlignment(jxl.format.VerticalAlignment.CENTRE); format.setBorder(Border.ALL,BorderLineStyle.THIN,Colour.BLACK); } catch (WriteException e) { // TODO 自動生成 catch 塊 e.printStackTrace(); } return format; } public static void main(String[] args) throws IOException, BiffException{ JexcelSample js = new JexcelSample(); File f = new File("D:\\address.xls"); f.createNewFile(); js.writeExc(f); js.readExc(f); } }
生成的excel表格以下:
4 jxl經常使用api 4.1.1 一、Workbook類提供的方法 int getNumberOfSheets() 獲取工做表的總個數 Sheet[] getSheets() 獲取數組型的工做表 Sheet getSheet(String name);//獲得此對應名稱的工做表 4.1.2 二、Sheet接口提供的方法 String getName() 獲取工做表的名稱 int getColumns() 獲取Sheet表中所包含的總列數 Cell[] getColumn(int column) 獲取某一列的全部單元格, 返回的是單元格對象數組 int getRows() 獲取Sheet表中所包含的總行數 Cell[] getRow(int row) 獲取某一行的全部單元格,返回的是單元格對象數組 Cell getCell(int column, int row)獲取指定單元格的對象引用,須要注意的是它的兩個參數,第一個是列數,第二個是行數,這與一般的行、列組合有些不一樣 WritableSheet.setRowView(int i,int height); 指定第i+1行的高度 WritableSheet.setColumnView(int i,int width); 指定第i+1列的寬度