由於項目中經常需要進行數據的導入和導出。所以研究下Excel的導入導出技術並作出整理。采用的是Apache POI 對Excel的支持。
Apache POI 是創建和維護操作各種符合Office Open XML(OOXML)標准和微軟的OLE 2復合文檔格式(OLE2)的Java API。用它可以使用Java讀取和創建,修改MS Excel文件.而且,還可以使用Java讀取和創建MS Word和MSPowerPoint文件。Apache POI 提供Java操作Excel解決方案(適用於Excel97-2008)。
其中 HSSF - 提供讀寫Microsoft Excel XLS格式檔案的功能 。XSSF - 提供讀寫Microsoft Excel OOXML XLSX格式檔案的功能。
網上搜索資料並進行了修改封裝:
1、ExcelBeanUtils.java 工具類
1 /** 2 * <p>Excel導入數據時進行bean的復制</p> 3 * -為Date類型注冊類型轉換器 4 * @version V1.0 5 */ 6 public class ExcelBeanUtils extends org.apache.commons.beanutils.BeanUtils{ 7 8 private static final String CONTEXT_KEY_FORMAT_DATE_VALUE = "yyyy-MM-dd"; 9 private static final String CONTEXT_KEY_FORMAT_DATETIME_VALUE = "yyyy-MM-dd HH:mm:ss"; 10 private static final String CONTEXT_KEY_FORMAT_TIME_VALUE = "HH:mm:ss"; 11 12 static { 13 DateConverter dateConverter = new DateConverter(null); 14 dateConverter.setUseLocaleFormat(true); 15 dateConverter.setPatterns(new String[]{CONTEXT_KEY_FORMAT_DATE_VALUE, CONTEXT_KEY_FORMAT_DATETIME_VALUE, CONTEXT_KEY_FORMAT_TIME_VALUE}); 16 ConvertUtils.register(dateConverter, Date.class); 17 } 18 19 public static class DateConverter extends DateTimeConverter { 20 21 public DateConverter() { 22 23 } 24 25 public DateConverter(Object defaultValue) { 26 super(defaultValue); 27 } 28 29 @SuppressWarnings("rawtypes") 30 protected Class getDefaultType() { 31 return Date.class; 32 } 33 34 @SuppressWarnings("rawtypes") 35 @Override 36 protected Object convertToType(Class type, Object obj) throws Exception { 37 if (obj == null) { 38 return null; 39 } 40 String value = obj.toString().trim(); 41 if (value.length() == 0) { 42 return null; 43 } 44 return super.convertToType(type, obj); 45 } 46 } 47 48 public static void populateBean(Object bean, Map<?, ?> properties) throws IllegalAccessException, InvocationTargetException{ 49 populate(bean, properties); 50 } 51 }
2、ExcelColumn.java 表頭信息和ExcelHead.java列信息
1 /** 2 * <p>Excel列信息</p> 3 * @author maxianming 2016-1-21 上午10:40:42 4 * @version V1.0 5 */ 6 public class ExcelColumn { 7 /** 8 * 列索引 9 */ 10 private Integer index; 11 /** 12 * 實際字段名稱 13 */ 14 private String fieldName; 15 /** 16 * 表格中的顯示名稱 17 */ 18 private String fieldDispName; 19 /** 20 * 字段類型。數字類型還是日期等 21 */ 22 private Integer type; 23 24 public ExcelColumn() { 25 26 } 27 28 public ExcelColumn(int index, String fieldName, String fieldDispName) { 29 this.index = index; 30 this.fieldName = fieldName; 31 this.fieldDispName = fieldDispName; 32 } 33 34 public ExcelColumn(int index, String fieldName, String fieldDispName, int type) { 35 this.index = index; 36 this.fieldName = fieldName; 37 this.fieldDispName = fieldDispName; 38 this.type = type; 39 } 40 41 public Integer getIndex() { 42 return index; 43 } 44 45 public void setIndex(Integer index) { 46 this.index = index; 47 } 48 49 public String getFieldName() { 50 return fieldName; 51 } 52 53 public void setFieldName(String fieldName) { 54 this.fieldName = fieldName; 55 } 56 57 public String getFieldDispName() { 58 return fieldDispName; 59 } 60 61 public void setFieldDispName(String fieldDispName) { 62 this.fieldDispName = fieldDispName; 63 } 64 65 public Integer getType() { 66 return type; 67 } 68 69 public void setType(Integer type) { 70 this.type = type; 71 } 72 73 }
ExcelHead.java 表頭信息
1 public class ExcelHead { 2 3 /** 4 * 列信息 5 */ 6 private List<ExcelColumn> columns; 7 8 /** 9 * 需要轉換的列 10 */ 11 private Map<String, Map<?, ?>> columnsConvertMap; 12 13 /** 14 * 頭部所占用的行數 15 */ 16 private int rowCount; 17 18 /** 19 * 頭部所占用的列數 20 */ 21 private int columnCount; 22 23 public List<ExcelColumn> getColumns() { 24 return columns; 25 } 26 27 public int getRowCount() { 28 return rowCount; 29 } 30 31 public int getColumnCount() { 32 return columnCount; 33 } 34 35 public void setColumns(List<ExcelColumn> columns) { 36 this.columns = columns; 37 } 38 39 public void setRowCount(int rowCount) { 40 this.rowCount = rowCount; 41 } 42 43 public void setColumnCount(int columnCount) { 44 this.columnCount = columnCount; 45 } 46 47 public Map<String, Map<?, ?>> getColumnsConvertMap() { 48 return columnsConvertMap; 49 } 50 51 public void setColumnsConvertMap(Map<String, Map<?, ?>> columnsConvertMap) { 52 this.columnsConvertMap = columnsConvertMap; 53 } 54 55 @Override 56 public String toString() { 57 return "ExcelHead [columnCount=" + columnCount + ", columns=" + columns 58 + ", columnsConvertMap=" + columnsConvertMap + ", rowCount=" 59 + rowCount + "]"; 60 } 61 62 }
3、ExcelHelper.java 導入導出的工具類(借鑒網上他人代碼) 采用泛型方式可直接導入相應對象類型的數據
8 package com.hikvision.finance.isms.common.excel; 9 10 import java.io.File; 11 import java.io.FileInputStream; 12 import java.io.FileNotFoundException; 13 import java.io.FileOutputStream; 14 import java.io.InputStream; 15 import java.lang.reflect.InvocationTargetException; 16 import java.math.BigDecimal; 17 import java.util.ArrayList; 18 import java.util.Date; 19 import java.util.HashMap; 20 import java.util.List; 21 import java.util.Map; 22 23 import org.apache.commons.lang3.StringUtils; 24 import org.apache.poi.openxml4j.exceptions.InvalidFormatException; 25 import org.apache.poi.ss.usermodel.Cell; 26 import org.apache.poi.ss.usermodel.DateUtil; 27 import org.apache.poi.ss.usermodel.Row; 28 import org.apache.poi.ss.usermodel.Sheet; 29 import org.apache.poi.ss.usermodel.Workbook; 30 31 import com.google.common.collect.Lists; 32 import com.hikvision.finance.core.util.BeanUtils; 33 import com.hikvision.finance.core.util.DateUtils; 34 import com.hikvision.finance.fwork.exception.ExpectedException; 35 import com.hikvision.finance.isms.common.excel.model.ExcelColumn; 36 import com.hikvision.finance.isms.common.excel.model.ExcelHead; 37 import com.hikvision.finance.isms.common.excel.utils.ExcelBeanUtils; 38 import com.hikvision.finance.isms.common.excel.utils.ExcelUtils; 39 40 /** 41 * <p>導入、導出的Excel幫助類</p> 42 * -導入數據到相關泛型對象T中 43 * @author maxianming 2016-1-21 下午12:14:35 44 * @version V1.0 45 */ 46 public class ExcelHelper<T> { 47 48 private IPoiExcelOperation excelOperation; 49 50 public IPoiExcelOperation getExcelOperation() { 51 return excelOperation; 52 } 53 54 public void setExcelOperation(IPoiExcelOperation excelOperation) { 55 this.excelOperation = excelOperation; 56 } 57 58 public ExcelHelper(IPoiExcelOperation excelOperation){ 59 this.excelOperation = excelOperation; 60 } 61 /** 62 * <p>將excel中數據導入到list中</p> 63 * 文件讀取失敗會拋出ExpectedException 64 * @author maxianming 2016-1-21 下午12:21:24 65 * @param head 文件頭信息 66 * @param file 導入的數據源 67 * @param cls 保存當前數據的對象 68 * @return 69 */ 70 public List<T> importToObjectList(ExcelHead head, File file, Class<T> cls) { 71 List<T> contents = null; 72 FileInputStream fis; 73 List<List<?>> rows = null; // 根據excel 每行 生成list類型的數據 74 try { 75 fis = new FileInputStream(file); 76 rows = excelFileConvertToList(fis); 77 } catch (Exception ex) { 78 ex.printStackTrace(); 79 throw new ExpectedException("","讀取文件失敗"); 80 } 81 // 1.刪除頭信息 82 if(rows != null){ 83 for (int i = 0; i < head.getRowCount(); i++) { 84 rows.remove(0); 85 } 86 } 87 // 2.將表結構轉換成Map 88 Map<Integer, String> excelHeadMap = convertExcelHeadToMap(head.getColumns()); 89 // 3.構建為對象 90 contents = buildDataObject(excelHeadMap, head.getColumnsConvertMap(), rows, cls); 91 return contents; 92 } 93 94 /** 95 * 將Excel文件內容轉換為List對象 96 * @author maxianming 2016-1-20 下午6:15:05 97 * @param fis excel文件 98 * @return List<List> list存放形式的內容 99 * @throws Exception 100 */ 101 public List<List<?>> excelFileConvertToList(FileInputStream fis) throws Exception{ 102 Workbook wb = this.excelOperation.readExcel(fis); 103 Sheet sheet = wb.getSheetAt(0); 104 List<List<?>> rows = new ArrayList<List<?>>(); 105 if(sheet != null){ 106 for (Row row : sheet) { 107 if(!ExcelUtils.isBlankRow(row)){ 108 List<Object> cells = new ArrayList<Object>(); 109 for (Cell cell : row) { 110 Object obj = null; 111 obj = this.getValue(cell); 112 cells.add(obj); 113 } 114 rows.add(cells); 115 } 116 } 117 } 118 return rows; 119 } 120 /** 121 * <p>將Excel中的數據類型進行轉換</P> 122 * @author maxianming 2016-1-21 下午6:27:54 123 * @param cell 124 * @return 125 */ 126 private Object getValue(Cell cell) { 127 Object value = null; 128 if(cell != null){ 129 switch (cell.getCellType()) { 130 case Cell.CELL_TYPE_STRING: 131 value = cell.getRichStringCellValue().getString(); 132 break; 133 case Cell.CELL_TYPE_NUMERIC: 134 if (DateUtil.isCellDateFormatted(cell)) { 135 value = cell.getDateCellValue(); 136 } else { 137 BigDecimal big = new BigDecimal(cell.getNumericCellValue()); 138 String strValue = big.toString(); 139 // 解決1234.0 去掉后面的.0 140 if(null != strValue && !"".equals(strValue.trim())){ 141 String[] item = strValue.split("[.]"); 142 if(1 < item.length && "0".equals(item[1])){ 143 strValue = item[0]; 144 } 145 }; 146 value = strValue; 147 } 148 break; 149 case Cell.CELL_TYPE_BOOLEAN: 150 value = cell.getBooleanCellValue(); 151 break; 152 case Cell.CELL_TYPE_FORMULA: 153 value = String.valueOf(cell.getNumericCellValue()); //讀公式計算值 154 if (value.equals("NaN")) { // 如果獲取的數據值為非法值,則轉換為獲取字符串 155 value = cell.getStringCellValue().toString(); 156 } 157 break; 158 default: 159 value = null; 160 } 161 } 162 return value; 163 } 164 165 /** 166 * <p>將報表結構轉換成Map</p> 167 * @author maxianming 2016-1-22 下午2:43:01 168 * @param excelColumns 169 * @return 170 */ 171 private Map<Integer, String> convertExcelHeadToMap(List<ExcelColumn> excelColumns) { 172 Map<Integer, String> excelHeadMap = new HashMap<Integer, String>(); 173 for (ExcelColumn excelColumn : excelColumns) { 174 if(StringUtils.isNotEmpty(excelColumn.getFieldName())) { 175 excelHeadMap.put(excelColumn.getIndex(), excelColumn.getFieldName()); 176 } 177 } 178 return excelHeadMap; 179 } 180 181 /** 182 * <p>根據Excel生成數據對象</P> 183 * @author maxianming 2016-1-21 下午1:42:22 184 * @param excelHeadMap 表頭信息 185 * @param excelHeadConvertMap 需要特殊轉換的單元 186 * @param rows Excel文件中數據的List對象 187 * @param cls 轉換為的對象 188 * @return 189 */ 190 private List<T> buildDataObject(Map<Integer, String> excelHeadMap, Map<String, Map<?, ?>> excelHeadConvertMap, List<List<?>> rows, Class<T> cls) { 191 List<T> contents = Lists.newArrayList(); 192 for (List<?> list : rows) { 193 // 1.如果當前第一列中無數據,則忽略當前行的數據 194 if(list == null || list.get(0) == null) { 195 break; 196 } 197 // 2.當前行的數據放入map中,生成<fieldName, value>的形式 198 Map<String, Object> rowMap = rowListToMap(excelHeadMap, excelHeadConvertMap, list); 199 // 3.將當前行轉換成對應的對象 200 T obj = null; 201 try { 202 203 obj = cls.newInstance(); 204 } catch (InstantiationException ex) { 205 ex.printStackTrace(); 206 } catch (IllegalAccessException ex) { 207 ex.printStackTrace(); 208 } 209 210 try { 211 ExcelBeanUtils.populateBean(obj, rowMap); 212 } catch (IllegalAccessException e) { 213 e.printStackTrace(); 214 throw new ExpectedException("","導入文件內容有誤!"); 215 } catch (InvocationTargetException e) { 216 e.printStackTrace(); 217 throw new ExpectedException("","導入文件內容有誤!"); 218 } 219 220 contents.add(obj); 221 } 222 return contents; 223 } 224 /** 225 * <p>將行轉行成map,生成<fieldName, value>的形式</p> 226 * @author maxianming 2016-1-21 下午1:46:57 227 * @param excelHeadMap 表頭信息 228 * @param excelHeadConvertMap 需要轉換的信息 229 * @param list excel中的數據 230 * @throws ExpectedException 當導入文件不是按模板定義好的格式時,拋出異常。 231 * @return 232 */ 233 private Map<String, Object> rowListToMap(Map<Integer, String> excelHeadMap, Map<String, Map<?, ?>> excelHeadConvertMap, List<?> list) { 234 Map<String, Object> rowMap = new HashMap<String, Object>(); 235 if(excelHeadMap.size() > list.size()){ 236 throw new ExpectedException("", "導入文件的內容格式有誤!"); 237 } 238 for(int i = 0; i < list.size(); i++) { 239 String fieldName = excelHeadMap.get(i); 240 if(fieldName != null) { 241 // 得到一行數據中每個單元格的value 242 Object value = list.get(i); 243 if(excelHeadConvertMap != null && excelHeadConvertMap.get(fieldName) != null) { 244 value = excelHeadConvertMap.get(fieldName).get(value); 245 } 246 rowMap.put(fieldName, value); 247 } 248 } 249 return rowMap; 250 } 251 252 /*--------------------------------導出功能還未完善-----------------------------------*/ 253 254 /** 255 * 導出數據至Excel文件 256 * @author maxianming 2016-1-20 下午6:41:30 257 * @param head 報表頭信息 258 * @param modelFile 導出文件 259 * @param outputFile 導出文件 260 * @param dataList 導入excel報表的數據來源 261 */ 262 public void exportExcelFile(ExcelHead head, File modelFile, File outputFile, List<?> dataList) { 263 InputStream inp = null; 264 Workbook wb = null; 265 try { 266 // 1.讀取導出excel模板 267 inp = new FileInputStream(modelFile); 268 wb = this.excelOperation.readExcel(inp); 269 Sheet sheet = wb.getSheetAt(0); 270 // 2.生成導出數據 271 buildExcelData(sheet, head, dataList); 272 273 // 3.導出到文件中 274 FileOutputStream fileOut = new FileOutputStream(outputFile); 275 wb.write(fileOut); 276 fileOut.close(); 277 } catch (FileNotFoundException ex) { 278 ex.printStackTrace(); 279 } catch (InvalidFormatException ex) { 280 ex.printStackTrace(); 281 } catch (Exception ex) { 282 ex.printStackTrace(); 283 } 284 } 285 /** 286 * <p>生成導出至Excel文件的數據</p> 287 * @author maxianming 2016-1-22 下午2:45:45 288 * @param sheet 工作區間 289 * @param head excel表頭 290 * @param dataList 導入excel報表的數據來源 291 */ 292 private void buildExcelData(Sheet sheet, ExcelHead head, List<?> dataList) { 293 List<ExcelColumn> excelColumns = head.getColumns(); 294 Map<String, Map<?, ?>> excelHeadConvertMap = head.getColumnsConvertMap(); 295 // 1.將表結構轉換成Map 296 Map<Integer, String> excelHeadMap = convertExcelHeadToMap(excelColumns); 297 // 2.從第幾行開始插入數據 298 int startRow = head.getRowCount(); 299 int order = 1; 300 for (Object obj : dataList) { 301 Row row = sheet.createRow(startRow++); 302 for (int j = 0; j < excelColumns.size(); j++) { 303 Cell cell = row.createCell(j); 304 cell.setCellType(excelColumns.get(j).getType()); 305 String fieldName = excelHeadMap.get(j); 306 if(fieldName != null) { 307 Object valueObject = null; 308 try { 309 valueObject = BeanUtils.getProperty(obj, fieldName); 310 } catch (Exception e){ 311 e.printStackTrace(); 312 } 313 /* 314 * 如果存在需要轉換的字段信息,則進行轉換 315 */ 316 if(excelHeadConvertMap != null && excelHeadConvertMap.get(fieldName) != null) { 317 valueObject = excelHeadConvertMap.get(fieldName).get(valueObject); 318 } 319 320 if(valueObject == null) { 321 cell.setCellValue(""); 322 } else if (valueObject instanceof Integer) { 323 cell.setCellValue((Integer)valueObject); 324 } else if (valueObject instanceof String) { 325 cell.setCellValue((String)valueObject); 326 } else if (valueObject instanceof Date) { 327 cell.setCellValue(DateUtils.getStringDateTime((Date)valueObject)); 328 } else { 329 cell.setCellValue(valueObject.toString()); 330 } 331 } else { 332 cell.setCellValue(order++); 333 } 334 } 335 } 336 } 337 338 }
4、ExcelHelper.java 工廠化方法按Excel格式產生特定的Excel導入導出工具
1 ** 2 * <p>產生一個ExcelHelper工具類</p> 3 * @author maxianming 2016-1-22 下午4:28:35 4 * @version V1.0 5 */ 6 public class ExcelHelperFactory { 7 /** 8 * <p>根據后綴名類型產生一個ExcelHelper類</p> 9 * @author maxianming 2016-1-22 下午4:30:41 10 * @param cls 11 */ 12 public static <T> ExcelHelper<T> createExcelHelper(String fileName){ 13 ExcelHelper<T> excelHelper = null; 14 if(StringUtils.isNotBlank(fileName)){ 15 String type = fileName.substring(fileName.lastIndexOf(".") + 1); 16 if("xls".equals(type)){ 17 excelHelper = new ExcelHelper<T>(new HSSFExcel()); 18 } else if("xlsx".equals(type)){ 19 excelHelper = new ExcelHelper<T>(new XSSFExcel()); 20 } else{ 21 throw new ExpectedException("","不支持Excel文件的擴展名【" + type +"】"); 22 } 23 } 24 return excelHelper; 25 } 26 }
最后實際應用例子:
// 1、Excel中每一行的信息
List<ExcelColumn> excelColumns = Lists.newArrayList();
excelColumns.add(new ExcelColumn(0, "strName", "機構名稱"));
excelColumns.add(new ExcelColumn(1, "nlevel","機構級別"));
excelColumns.add(new ExcelColumn(2, "strCode", "機構號"));
excelColumns.add(new ExcelColumn(3, "strAddress","機構地址"));
excelColumns.add(new ExcelColumn(4, "parentOrg", "上級機構"));
// 2、機構級別中顯示的名稱-數據庫中整數
Map<String, Integer> levelMap = new HashMap<String, Integer>(){
{
put("總行", 0); put("一級分行", 1); put("二級分行", 2);
put("直屬支行", 3); put("支行", 4); put("辦事處", 5);
put("經營支行", 6); put("分理處", 7); put("儲蓄所",8);
}
};
// 3、excel中顯示的信息轉換為數據庫中的值
Map<String, Map<?, ?>> excelColumnsConvertMap = Maps.newHashMap();
excelColumnsConvertMap.put("nlevel", levelMap);
// 4、組裝excel信息
ExcelHead excelHead = new ExcelHead();
excelHead.setColumnCount(2);
excelHead.setColumns(excelColumns);
excelHead.setColumnsConvertMap(excelColumnsConvertMap);
// 5、Excel導入類使用
List<OrganizationDto> orgs = Lists.newArrayList();
ExcelHelper<OrganizationDto> excelHelper = ExcelHelperFactory.createExcelHelper(fileName);
orgs = excelHelper.importToObjectList(excelHead, orgFile, OrganizationDto.class);