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

poi
poi

本文是使用 org.apache.poi 進行一次簡單的封裝,適用於大部分 excel 導入導出功能。過程當中可能會用到反射,如如有對於性能有極致強迫症的同窗,看看就好。java

因爲 poi 自己只是針對於 excel 等office軟件的一個工具包,在一些常規的 excel 導入導出時,還須要再作一次精簡的封裝,簡化代碼耦合。apache

1、現狀

本人經歷過幾家公司的代碼封裝,導入導出通常存在下面的狀況。json

1.1 導入

  1. 傳入文件地址,返回 Sheet 對象,在業務代碼中進行循環遍歷,作相對應的類型轉換,業務處理(二零零幾年的代碼框架)
  2. 傳入文件地址,返回 List<String, Object> 的對象,外部直接作強轉
  3. 傳入文件地址,返回 List<String, String> 的對象,外部將字符串對象轉換爲對應的類型

總結:若是隻有上述的選擇,本人是比較傾向於第二種,畢竟對外層是很是友好的api

1.2 導出

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

總結:若是隻有上述的選擇,本人是比較傾向於第三種,第三種只遍歷一次,而且外部未作處理。可是按第四種模式來看,那麼第三種模式仍是會存在日期格式問題,這個咱們後續再分析如何處理。數組

2、導入

2.1 方法定義

/** * excel導入 * @param keys 字段名稱數組,如 ["id", "name", ... ] * @param filePath 文件物理地址 * @return * @author yzChen * @date 2016年12月18日 下午2:46:51 */
public static List<Map<String, Object>> imp(String filePath, String[] keys)
    throws Exception {}
複製代碼

2.2 循環處理模塊

// 遍歷該行全部列
for (short j = 0; j < 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() > 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<Map<String, Object>> impList;
try {
    impList = ExcelUtil.imp(filePath, keys);
    
    for (Map<String, Object> map : impList) {
        System.out.println(map.get("brand"));
    }
} catch (Exception e) {
    e.printStackTrace();
}
複製代碼

2.4 分析

  1. 入口只須要傳入文件名稱,以及外部須要讀取的key便可
  2. 內部處理,則針對 數值型、日期類型、字符串 類型已經作了對應處理,外部則直接進行強轉對應的類型便可

3、導出

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 <T> File export(String fileNamePath, String sheetName, List<T> list, String[] titles, String[] fieldNames) throws Exception {}
複製代碼

3.2 循環處理模塊

// 遍歷生成數據行,經過反射獲取字段的get方法
for (int i = 0; i < list.size(); i++) {
    t = list.get(i);
    HSSFRow row = sheet.createRow(i+1);
    Class<? extends Object> clazz = t.getClass();
    for(int j = 0; j < 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 --> " + methodName);
        }
        ret = null == method.invoke(t) ? null : method.invoke(t) + "";
        setCellGBKValue(row.createCell(j), ret + "");
    }
}
複製代碼

3.3 使用

String[] titles = new String[]{"Id", "Brand"};
String[] fieldNames = new String[]{"id", "brand"};
List<Order> expList = new ArrayList<Order>();
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 數據集合,以及 fieldNames 字段名稱
  2. 內部處理,是直接經過反射得到 get 方法的返回值,進行強轉爲字符串進行導出
  3. 爲了兼容繼承父類的一些共有字段的設計,則加上了一層父類的方法讀取

4、關於日期類型導出處理

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;
}

複製代碼

My Blog

blog.guijianpan.com框架

技術交流

相關文章
相關標籤/搜索