Apache POI For Java Excel

Apache POI For Java Excel<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

POI的主頁:http://jakarta.apache.org/poi

 POI HSSF的Quick Guide,教初學者如何快速上手使用POI HSSF:

http://jakarta.apache.org/poi/hssf/quick-guide.html

 筆者據使用經驗以爲:POI HSSF是當今市面上最強大的處理EXCEL表格的java工具,比韓國人寫的那個JExcelApi或其它幾種工具都要好。而且它是Apache的開源項目。當然POI HSSF也有缺點:不能直接支持EXCEL圖表,API文檔粗糙簡略,有些類和方法需要引用Apache項目中的其它一些包,包與包之間依賴關係比較複雜等等。

 本文內容不在於提供一個POI HSSF 的完整使用指南(上面那個Apache主頁上的Quick Guide已經非常詳細),而是列出一些筆者在項目開發過程中找到的一些技巧、經驗。

目前POI版本爲2.5.1,org.apache.poi.hssf.usermodel包裏有一個HSSFChart類,裏面只有一個空方法createBarChart(),表明POI還不直接支持EXCEL圖表。

替代方法還是有的:因爲EXCEL圖表的源數據引用自EXCEL單元格。我們可以先新建一個EXCEL工作薄,作爲模板,在裏面創建圖表,指定它引用工作表中的一些特定單元格。然後我們用POI來讀取這個工作薄,把數據寫入到那些特定單元格。

首先要在模板裏創建可以動態引用單元格的名稱,利用名稱來創建圖表中的系列 

一、打開模板PoiTest.xls,點擊[插入]>[名稱]>[定義],創建四個“名稱”sx,s1y,s2y,s3y:

sx=OFFSET(Sheet1!$A$17,0,2,1,COUNTA(Sheet1!$17:$17)-4)

s1y=OFFSET(Sheet1!$A$18,0,2,1,COUNTA(Sheet1!$18:$18)-4)

s2y=OFFSET(Sheet1!$A$19,0,2,1,COUNTA(Sheet1!$19:$19)-3)

s3y=OFFSET(Sheet1!$A$20,0,2,1,COUNTA(Sheet1!$20:$20)-3)

這裏用到了兩個EXCEL函數,OFFSET()和COUNTA()函數。

其中COUNTA()可以返回一行或一列的單元格總數:

比如COUNTA(Sheet1!$A:$A),計算工作表Sheet1的A列的單元格數目。

又比如COUNTA(Sheet1!$17:$17),計算的是Sheet1的第17行的單元格數目。

當我們沒有在單元格里鍵入數據時,該單元格是不會被COUNTA()計算的。

OFFSET()函數用來引用一系列連續的單元格,它共有五個參數:

參數一,作爲位置參照的單元格。

參數二,行的起始偏移量(以參數一爲參照)。

參數三,列的起始偏移量(以參數一爲參照)。

參數四,跨行數。

參數五,跨列數。

比如:

OFFSET(Sheet1!$A:$1,1,2,3,4),表示引用範圍爲:C2:F4。 

二、在模板中創建圖表,在圖表上點右鍵,選擇[源數據]>[系列],如圖建立三個系列:

[添加]創建新的系列:

[名稱]表示系列名,可以直接輸入字串,也可以引用EXCEL單元格。

[]中輸入我們在上一步中創建的名稱,格式爲:模板名.xls!名稱。

[分類(X)軸標誌(T)]中輸入我們在上一步中創建的名稱”sx,格式爲:模板名.xls!名稱。它表示圖表區域的X軸將要顯示的內容。 

三、用POI把數據寫入到相應的單元格中,圖表將會自動顯示對應的信息。 

注意:

上面這種方法適用數據集合行數固定而列數動態變化的情況。

對於行數也動態變化的情況,只能先在模板裏預設儘可能多的名稱系列

對於行數和列數都固定的情形,沒必要這麼複雜,只要在圖表的[源數據]裏設置[數據區域],使之引用EXCEL模板中的一定範圍,如下圖:

設置單元格樣式 

HSSFCellStyle類代表一種單元格樣式。可以通過這個類來設置單元格的邊框樣式背景顏色字體水平垂直對齊方式等等。

 

HSSFCellStyle titleStyle = workbook.createCellStyle();
  titleStyle.setBorderBottom(HSSFCellStyle.BORDER_DOUBLE);
  titleStyle.setBorderLeft((short)1);
  titleStyle.setBorderRight((short)1);
  titleStyle.setBorderTop(HSSFCellStyle.BORDER_DOUBLE);
  titleStyle.setFillForegroundColor(HSSFColor.LIGHT_ORANGE.index);
  titleStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);

 

注意:如果我們定義了一種樣式,把它賦給一些單元格。然後基於新的需要,更改該樣式中的某個屬性,再賦給另一些單元格。那麼之前單元格樣式的該屬性也會被同時更改。

比如我們定義了樣式,設置單元格背景色爲紅色: 

HSSFCellStyle cellStyle = workbook.createCellStyle();

cellStyle.setFillForegroundColor(HSSFColor.RED.index);
cellStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);

 然後把它賦給一個單元格: 

HSSFCell cell1 = row.createCell((short)1);

cell1.setCellStyle(cellStyle);

 然後更改樣式中的背景色屬性爲藍色: 

cellStyle.setFillForegroundColor(HSSFColor.BLUE.index);

 然後賦給另一個單元格: 

HSSFCell cell2 = row.createCell((short)2);

cell2.setCellStyle(cellStyle);

 想當然,我們預計在最終結果中cell1的背景色爲紅色,cell2的背景色爲藍色。但是結果是:兩個單元格的背景色都變成了藍色。 

遇到這種情況,要預先定義兩種不同的單元格樣式。 

當一個EXCEL文件同時需要很多大同小異的單元格樣式時,這樣一一定義很麻煩。POI HSSF提供了一個HSSFCellUtil類(在org.apache.poi.hssf.usermodel.contrib包),裏面有幾個方法可以繞過HSSFCellStyle直接設定單元格的樣式,但這幾個方法會拋出NestableException異常,要處理這個異常,需要引用Apache的幾個Common包:

commons-beanutils.jar

commons-beanutils-bean-collections.jar

commons-beanutils-core.jar

commons-lang.jar

commons-logging-api.jar 

合併單元格

HSSFSheet.addMergedRegion(new Region())方法可以合併單元格,Region()中的一個構造函數含有四個參數,分別代表起始行、起始列、結束行、結束列:

sheet.addMergedRegion(new Region(initRow, (short)(initCol-2), initRow + lists.size() - 1, (short)(initCol-2)));

 處理公式 

HSSFCell.setCellFormula()方法用來在EXCEL單元格中寫入公式。

   cell = row.createCell((short)(dataFlag));
   cell.setCellType(HSSFCell.CELL_TYPE_FORMULA);
   cell.
setCellFormula("SUM(" + getColLetter(initCol) + (listFlag+1) +
     ":" + getColLetter(dataFlag-1) + (listFlag+1) + ")");

   cell.setCellStyle(nameStyle);

 處理鏈接 

POI中往單元格中寫鏈接,是用HYPERLINK函數搞定的。

HYPERLINK函數包含兩個參數,第一個參數是指向的URL地址,第二個參數是顯示字串。 

   cell = row.createCell((short)(dataFlag));
   cell.setCellType(HSSFCell.CELL_TYPE_FORMULA);
   cell.setCellFormula("HYPERLINK(/"
http://www.xxxxx.com/xxx.jsp?id=1/",/"homepage/")");
   cell.setCellStyle(linkStyle);

 爲了使鏈接效果更好,我們可以給鏈接所在單元格定義一種樣式,使鏈接顯示爲有下劃線的藍色字串: 

  HSSFCellStyle linkStyle = workbook.createCellStyle();
  linkStyle.setBorderBottom((short)1);
  linkStyle.setBorderLeft((short)1);
  linkStyle.setBorderRight((short)1);
  linkStyle.setBorderTop((short)1);
  linkStyle.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
  linkStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
  HSSFFont font = workbook.createFont();
  font.setFontName(HSSFFont.FONT_ARIAL);
  
font.setUnderline((byte)1);
  font.setColor(HSSFColor.BLUE.index);
  linkStyle.setFont(font);

 中文處理: 

要在通過POI生成的EXCEL中正常顯示中文,需要爲單元格設置編碼:

 cell.setEncoding(HSSFCell.ENCODING_UTF_16);
  cell.setCellValue("
部門");

 完整的PoiServlet類:

package org.eleaf.poi.servlets;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.hssf.util.Region;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;

public class PoiServlet extends HttpServlet {


 public void doGet(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  response.setContentType("application/vnd.ms-excel;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=PoiTest.xls");
        ServletOutputStream sos = response.getOutputStream();
        HSSFWorkbook workbook = new HSSFWorkbook(getServletContext().getResourceAsStream("/PoiTest.xls"));
        HashMap map = getDatas();
        workbook = writeDatas(workbook, map);
        workbook.write(sos);
        sos.close(); 
 }
 /**
  * 將數據寫入到EXCEL中。
  * @param workbook
  * @param map 數據集合
  * @return
  */
 private HSSFWorkbook writeDatas(HSSFWorkbook workbook, HashMap map)
 {
  HSSFCellStyle titleStyle = workbook.createCellStyle();
  titleStyle.setBorderBottom(HSSFCellStyle.BORDER_DOUBLE);
  titleStyle.setBorderLeft((short)1);
  titleStyle.setBorderRight((short)1);
  titleStyle.setBorderTop(HSSFCellStyle.BORDER_DOUBLE);
  titleStyle.setFillForegroundColor(HSSFColor.LIGHT_ORANGE.index);
  titleStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
  HSSFCellStyle dataStyle = workbook.createCellStyle();
  dataStyle.setBorderBottom((short)1);
  dataStyle.setBorderLeft((short)1);
  dataStyle.setBorderRight((short)1);
  dataStyle.setBorderTop((short)1);
  dataStyle.setFillForegroundColor(HSSFColor.LIGHT_GREEN.index);
  dataStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
  dataStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
  HSSFCellStyle nameStyle = workbook.createCellStyle();
  nameStyle.setBorderBottom((short)1);
  nameStyle.setBorderLeft((short)1);
  nameStyle.setBorderRight((short)1);
  nameStyle.setBorderTop((short)1);
  nameStyle.setFillForegroundColor(HSSFColor.LIGHT_CORNFLOWER_BLUE.index);
  nameStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
  HSSFCellStyle linkStyle = workbook.createCellStyle();
  linkStyle.setBorderBottom((short)1);
  linkStyle.setBorderLeft((short)1);
  linkStyle.setBorderRight((short)1);
  linkStyle.setBorderTop((short)1);
  linkStyle.setFillForegroundColor(HSSFColor.SKY_BLUE.index);
  linkStyle.setFillPattern(HSSFCellStyle.SOLID_FOREGROUND);
  HSSFFont font = workbook.createFont();
  font.setFontName(HSSFFont.FONT_ARIAL);
  font.setUnderline((byte)1);
  font.setColor(HSSFColor.BLUE.index);
  linkStyle.setFont(font);
  
  HSSFSheet sheet = workbook.getSheetAt(0);
  final int initRow = 17;
  final int initCol = 2;
  HSSFRow rTitle = sheet.createRow(initRow - 1);
  List lists = (List) map.get("list");
  List titles = (List)map.get("title");
  int titleFlag = initCol;
  for (Iterator it = titles.iterator(); it.hasNext();)
  {
   String title = (String)it.next();
   HSSFCell cell = rTitle.createCell((short)titleFlag);
   cell.setCellStyle(titleStyle);
   cell.setEncoding(HSSFCell.ENCODING_UTF_16);
   cell.setCellType(HSSFCell.CELL_TYPE_STRING);
   cell.setCellValue(title);
   titleFlag++;
  }
  
  HSSFCell cell = rTitle.createCell((short)(titleFlag));
  cell.setCellStyle(titleStyle);
  cell.setCellType(HSSFCell.CELL_TYPE_STRING);
  cell.setEncoding(HSSFCell.ENCODING_UTF_16);
  cell.setCellValue("總計");
  titleFlag++;
  
  cell = rTitle.createCell((short)(titleFlag));
  cell.setCellStyle(titleStyle);
  cell.setCellType(HSSFCell.CELL_TYPE_STRING);
  cell.setEncoding(HSSFCell.ENCODING_UTF_16);
  cell.setCellValue("鏈接");
  
  cell = rTitle.createCell((short)(initCol-1));
  cell.setCellStyle(titleStyle);
  cell.setCellType(HSSFCell.CELL_TYPE_STRING);
  cell.setEncoding(HSSFCell.ENCODING_UTF_16);
  cell.setCellValue("職員");
  
  cell = rTitle.createCell((short)(initCol-2));
  cell.setCellStyle(titleStyle);
  cell.setCellType(HSSFCell.CELL_TYPE_STRING);
  cell.setEncoding(HSSFCell.ENCODING_UTF_16);
  cell.setCellValue("部門");
  int listFlag = initRow;
  for (Iterator it = lists.iterator(); it.hasNext();)
  {
   String name = (String)it.next();
   List datas = (List)map.get(name);
   HSSFRow row = sheet.createRow(listFlag);
   
   cell = row.createCell((short)(initCol-1));
   cell.setCellStyle(nameStyle);
   cell.setEncoding(HSSFCell.ENCODING_UTF_16);
   cell.setCellValue(name);
   
   cell = row.createCell((short)(initCol-2));
   cell.setEncoding(HSSFCell.ENCODING_UTF_16);
   cell.setCellStyle(dataStyle);
   int dataFlag = initCol;
   System.out.println("datas=" + datas);
   for (Iterator ite = datas.iterator(); ite.hasNext();)
   {
    int data = ((Integer)ite.next()).intValue();
    cell = row.createCell((short)dataFlag);
    cell.setCellStyle(dataStyle);
    cell.setCellType(HSSFCell.CELL_TYPE_NUMERIC);
    cell.setCellValue(data);
    dataFlag++;
   }
   cell = row.createCell((short)(dataFlag));
   cell.setCellType(HSSFCell.CELL_TYPE_FORMULA);
   cell.setCellFormula("SUM(" + getColLetter(initCol) + (listFlag+1) +
     ":" + getColLetter(dataFlag-1) + (listFlag+1) + ")");
   cell.setCellStyle(nameStyle);
   dataFlag++;
   
   cell = row.createCell((short)(dataFlag));
   cell.setCellType(HSSFCell.CELL_TYPE_FORMULA);
   cell.setCellFormula("HYPERLINK(/"
http://www.xxxxx.com/xxx.jsp?id=1/",/"homepage/")");
   cell.setCellStyle(linkStyle);
   listFlag++;
  }
  sheet.getRow(initRow).getCell((short)(initCol-2)).setCellValue("武裝部");
  
  sheet.addMergedRegion(new Region(initRow, (short)(initCol-2), initRow + lists.size() - 1, (short)(initCol-2)));
  return workbook;
 }
 /**
  * 將列的索引換算成ABCD字母,這個方法要在插入公式時用到。
  * @param colIndex 列索引。
  * @return ABCD字母。
  */
 private String getColLetter(int colIndex)
 {
  String ch = "";
     if (colIndex  < 26)
         ch = "" + (char)((colIndex) + 65);
     else
        ch = "" + (char)((colIndex) / 26 + 65 - 1) + (char)((colIndex) % 26 + 65);
     return ch;
 }
 /**
  * 獲得數據,組織爲HashMap. 這裏爲了演示方便,簡單生成了一些數據。
在實際應用中,是從
  *
數據庫中獲取數據的。
 
* @return 組織後的數據
  */
 private HashMap getDatas()
 {
  HashMap map = new HashMap();
  List lists = new ArrayList();
  List title = new ArrayList();
  List a = new ArrayList();
  List b = new ArrayList();
  List c = new ArrayList();
  for (int i = 1; i <= 8; i++)
  {
   title.add(i + "月");
   a.add(new Integer((int)(Math.random() * 10)));
   b.add(new Integer((int)(Math.random() * 10)));
   c.add(new Integer((int)(Math.random() * 10)));
  }
  map.put("荊軻", a);lists.add("荊軻");
  map.put("專諸", b); lists.add("專諸");
  map.put("聶政", c); lists.add("聶政");
  map.put("list", lists);
  map.put("title", title);
  System.out.println("map=" + map);
  return map;
 }
 public void doPost(HttpServletRequest request, HttpServletResponse response)
   throws ServletException, IOException {
  doGet(request, response);
 }
 
 

}

 

發佈了28 篇原創文章 · 獲贊 1 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章