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

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<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、導入

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<Model> 數據集合,以及 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;
}

5、Demo下載

GJP-Example-ExcelUtil 代碼下載框架

My Blog

blog.guijianpan.com工具

技術交流

相關文章
相關標籤/搜索