1. 程式人生 > >基於 POI 封裝 ExcelUtil 精簡的 Excel 匯入匯出

基於 POI 封裝 ExcelUtil 精簡的 Excel 匯入匯出

由於 poi 本身只是針對於 excel 等office軟體的一個工具包,在一些常規的 excel 匯入匯出時,還需要再做一次精簡的封裝,簡化程式碼耦合。

一、現狀

本人經歷過幾家公司的程式碼封裝,匯入匯出一般存在下面的情況。

1.1 匯入

  1. 傳入檔案地址,返回 Sheet 物件,在業務程式碼中進行迴圈遍歷,做相對應的型別轉換,業務處理(二零零幾年的程式碼框架)
  2. 傳入檔案地址,返回 List<String, Object> 的物件,外部直接做強轉
  3. 傳入檔案地址,返回 List<String, String> 的物件,外部將字串物件轉換為對應的型別

總結:如果只有上述的選擇,本人是比較傾向於第二種,畢竟對外層是非常友好的

1.2 匯出

  1. 直接在邏輯程式碼中進行遍歷封裝sheet,傳入到生成file的方法中(二零零幾年的程式碼框架)
  2. 先迴圈遍歷 List<Model> 物件,轉換為 List<Map<String, String>> 物件,帶上 fieldName 傳入到封裝好excel生成的方法中,內部則使用 map.get() 方法操作
  3. 直接將 List<Model> 物件帶上 fieldName 傳入到封裝好excel生成的方法中,內部將 Model 物件轉換為 JSONObject,然後使用 jsonObj.get() 方法操作
  4. 先將 List<Model> 轉換為 JSONArray ,帶上 fieldName 傳入到封裝好excel生成的方法中,內部將 Model 物件轉換為 JSONObject,然後使用 jsonObj.get() 方法操作。(使用這種做法,據分析應該是為了執行 jsonConfig.registerJsonValueProcessor(Date.class, new JsonDateValueProcessor(“yyyy-MM-dd HH:mm:ss”)); 這行程式碼,可能是為了解決日期型別格式問題)

總結:如果只有上述的選擇,本人是比較傾向於第三種,第三種只遍歷一次,並且外部未做處理。但是按第四種模式來看,那麼第三種模式還是會存在日期格式問題,這個我們後續再分析如何處理。

二、匯入

2.1 方法定義

/**
* excel匯入
* @param keys       欄位名稱陣列,如  ["id", "name", ... ]
* @param filePath   檔案實體地址
* @return 
* @author yzChen
* @date 2016年12月18日 下午2:46:51
*/
public static List&lt;Map&lt;String, Object&gt;&gt; imp(String filePath, String[] keys)
    throws Exception {}

2.2 迴圈處理模組

// 遍歷該行所有列
for (short j = 0; j &lt; cols; j++) {
    cell = row.getCell(j);
    if(null == cell) continue;  // 為空時,下一列

    // 根據poi返回的型別,做相應的get處理
    if(Cell.CELL_TYPE_STRING == cell.getCellType()) {
        value = cell.getStringCellValue();
    } else if(Cell.CELL_TYPE_NUMERIC == cell.getCellType()) {
        value = cell.getNumericCellValue();

        // 由於日期型別格式也被認為是數值型,此處判斷是否是日期的格式,若時,則讀取為日期型別
        if(cell.getCellStyle().getDataFormat() &gt; 0)  {
            value = cell.getDateCellValue();
        }
    } else if(Cell.CELL_TYPE_BOOLEAN == cell.getCellType()) {
        value = cell.getBooleanCellValue();
    } else if(Cell.CELL_TYPE_BLANK == cell.getCellType()) {
        value = cell.getDateCellValue();
    } else {
        throw new Exception("At row: %s, col: %s, can not discriminate type!");
    }

    map.put(keys[j], value);
}

2.3 使用

String filePath = "E:/order.xls";
String[] keys = new String[]{"id","brand"};

List&lt;Map&lt;String, Object&gt;&gt; impList;
try {
    impList = ExcelUtil.imp(filePath, keys);

    for (Map&lt;String, Object&gt; map : impList) {
        System.out.println(map.get("brand"));
    }
} catch (Exception e) {
    e.printStackTrace();
}

2.4 分析

  1. 入口只需要傳入檔名稱,以及外部需要讀取的key即可
  2. 內部處理,則針對 數值型、日期型別、字串 型別已經做了對應處理,外部則直接進行強轉對應的型別即可

三、匯出

3.1 方法定義

/**
* excel匯出
* @param fileNamePath   匯出的檔名稱
* @param sheetName  匯出的sheet名稱
* @param list       資料集合
* @param titles     第一行表頭
* @param fieldNames 欄位名稱陣列
* @return
* @throws Exception    
* @author yzChen
* @date 2017年5月6日 下午3:53:47
*/
public static &lt;T&gt; File export(String fileNamePath, String sheetName, 
    List&lt;T&gt; list, String[] titles, String[] fieldNames) throws Exception {}

3.2 迴圈處理模組

// 遍歷生成資料行,通過反射獲取欄位的get方法
for (int i = 0; i &lt; list.size(); i++) {
    t = list.get(i);
    HSSFRow row = sheet.createRow(i+1);
    Class&lt;? extends Object&gt; clazz = t.getClass();
    for(int j = 0; j &lt; fieldNames.length; j++){
        methodName = "get" + capitalize(fieldNames[j]);
        try {
            method = clazz.getDeclaredMethod(methodName);
        } catch (java.lang.NoSuchMethodException e) {   //  不存在該方法,檢視父類是否存在。此處只支援一級父類,若想支援更多,建議使用while迴圈
            if(null != clazz.getSuperclass()) {
                method = clazz.getSuperclass().getDeclaredMethod(methodName);
            }
        }
        if(null == method) {
            throw new Exception(clazz.getName() + " don't have menthod --&gt; " + methodName);
        }
        ret = method.invoke(t);
        setCellGBKValue(row.createCell(j), ret + "");
    }
}

3.3 使用

String[] titles = new String[]{"Id", "Brand"};
String[] fieldNames = new String[]{"id", "brand"};
List&lt;Order&gt; expList = new ArrayList&lt;Order&gt;();
Order order = new Order();
order.setId(1L);
order.setBrand("第三方手動閥");
expList.add(order);
order = new Order();
order.setId(2L);
order.setBrand("scsdsad");
expList.add(order);

String fileNamePath = "E:/order.xls";
try {
    ExcelUtil.export(fileNamePath, "訂單", expList, titles, fieldNames);
} catch (Exception e) {
    e.printStackTrace();
}

3.4 總結

  1. 入口主要是需要傳入 List<Model> 資料集合,以及 fieldNames 欄位名稱
  2. 內部處理,是直接通過反射獲得 get 方法的返回值,進行強轉為字串進行匯出
  3. 為了相容繼承父類的一些共有欄位的設計,則加上了一層父類的方法讀取

四、關於日期型別匯出處理

1.1 日期欄位匯出指定格式內容

  1. 建議在 Model 類中,新增一個擴充套件欄位,並封裝一個 get 方法,內容則只是對原欄位進行轉換,匯出時,fieldName 則傳遞擴充套件欄位即可。如 createTime,示例如下:
    private Date createTime;
    private String createTimeStr;   // 擴充套件欄位
    

public Date getCreateTime() {

return createTime;

}

public void setCreateTime(Date createTime) {

this.createTime = createTime;

}

public String getCreateTimeStr() {

createTimeStr = DateUtil.formatDatetime(this.createTime);
return createTimeStr;

}

## 五、Demo下載
[GJP-Example-ExcelUtil 程式碼下載](https://pan.baidu.com/s/1jIzFFPO)