最近,公司在作導入導出的項目,首先想到的是poi的導入和導出。若是每次導入和導出都要重寫的話,那麼,實在是浪費時間和精力。因而,封裝了原生的poi的導入和導出。在封裝的時候,就會出現一系列的問題。java
在進行導入和導出的時候,咱們必需要熟悉Excel,尤爲是它所支持的類型,如圖所示:數據庫
由於我用的是java,這要和java語言相匹配。java有八大基本類型(包裝類型)、字符串類型、日期類型、時間戳類型等。Excel類型要和這些類型相匹配,不然,就會出現導入和導出的問題。json
一、 由於HashMap的key值是對象的hashCode值,hashCode值是散列值。這樣存儲方式是散列存儲,就像浴缸中的魚同樣,它的位置是不肯定的,於是,輸出結果具備不少不肯定性。也就是說,輸出結果的順序和咱們存儲的順序不一致,如圖所示:mybatis
二、可是,LinkedHashMap採用的是鏈表,鏈表節點的存儲的是下一個引用對象的首地址。就像是一根繩子上節點,系在哪兒就在哪那兒,位置不會改變的,如圖所示:app
/** * Created By zby on 18:22 2019/3/9 * * @param response 響應客戶端的導出請求 * @param headerMap 建立表頭 * @param dataList 數據集 * @param excelName excel表名 */ public static void exportSimpleExcel(HttpServletResponse response, LinkedHashMap<String, String> headerMap, List<JSONObject> dataList, String excelName) { // 建立個workbook XSSFWorkbook workbook = new XSSFWorkbook(); // 建立一個sheet XSSFSheet sheet = workbook.createSheet(); Pattern chinese_pattern = Pattern.compile("[\\u4e00-\\u9fa5]"); if (sheet != null) { try { // 寫數據 Integer i = 0; for (Map.Entry<String, String> entry : headerMap.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); Row headRow = sheet.getRow(0); if (isNull(headRow)) { headRow = sheet.createRow(0); } CellStyle cellStyle = workbook.createCellStyle(); cellStyle.setBorderBottom(HSSFCellStyle.BORDER_THIN); //下邊框 cellStyle.setBorderLeft(HSSFCellStyle.BORDER_THIN);//左邊框 cellStyle.setBorderTop(HSSFCellStyle.BORDER_THIN);//上邊框 cellStyle.setBorderRight(HSSFCellStyle.BORDER_THIN);//右邊框 cellStyle.setAlignment(HSSFCellStyle.ALIGN_CENTER);//居中 cellStyle.setFillBackgroundColor(HSSFColor.GREY_40_PERCENT.index); //建立標題行,若是第一遍已經建立了,就直接根據下標獲取行, // 若是沒有建立標題行,就根據當前下標建立當前行,如下相同。 Cell headCell = headRow.createCell(i); headCell.setCellValue(value); headCell.setCellStyle(cellStyle); //設置標題下的數據,每建立標題的一個單元格,就建立該單元格下的全部列 //行數就是一行標題單元格+數據單元格的個數,即merginRow+j for (int j = 0; j < dataList.size(); j++) { Object obj = dataList.get(j); //須要判斷json是不是jsonObject的實例化對象,別人在調用這個方法時, // 咱們不清楚jsons集合中是否存在其餘類的對象,由於須要做個驗證 Object val = null; if (obj instanceof JSONObject) { JSONObject json = (JSONObject) obj; val = json.get(key); } // else {這裏若是不是JsonObject對象,就用這種方式獲取 // try { // //這是appach下的方法,其經過屬性名稱,反射獲得當前對象屬性的數值 // //json當中的key就至關於其屬性,其值就是value值 // val = getProperty(obj, key); // } catch (Exception e) { // if (obj != null) { // LogUtil.warn(logger, "類: " + obj.getClass() + ", 屬性: " + key); // } // val = null; // } // } Row row = sheet.getRow(1 + j); if (isNull(row)) { row = sheet.createRow(1 + j); } Cell cell = row.createCell(i); cell.setCellStyle(cellStyle); if (val instanceof Double) { cell.setCellType(Cell.CELL_TYPE_NUMERIC); cell.setCellValue(((Double) val).doubleValue()); } else if (val instanceof Integer) { cell.setCellType(Cell.CELL_TYPE_NUMERIC); cell.setCellValue(((Integer) val).intValue()); } else if (val instanceof Date) { cell.setCellValue(ISO_DATETIME_FORMAT.format(val)); } else if (val instanceof Calendar) { cell.setCellValue((Calendar) val); } else if (val instanceof Boolean) { cell.setCellType(Cell.CELL_TYPE_BOOLEAN); cell.setCellValue(((Boolean) val).booleanValue()); } else if (val instanceof String || val instanceof RichTextString) { cell.setCellType(Cell.CELL_TYPE_NUMERIC); String cellValue = (String) val; int width = cellValue.length(); Matcher matcher = chinese_pattern.matcher(cellValue); while (matcher.find()) { width++; } width = (width > 6) ? ((width < 80) ? width : 80) : 6; sheet.setColumnWidth(i, (256 * width + 184) + 500); cell.setCellValue(cellValue); } else { cell.setCellValue(""); } } i++; } String realPath = (getSispPath() + "uploadExcelRecord"); // 新的文件名 String newFileName = excelName + DateUtil.ISO_DATETIME_FORMAT_NONE.format(new Date()) + ".xlsx"; // 判斷路徑是否存在 File dir = new File(realPath); if (!dir.exists()) { dir.mkdirs(); } // 寫入到新的excel File newFile = new File(realPath, newFileName); FileOutputStream fos = new FileOutputStream(newFile); workbook.write(fos); fos.flush(); fos.close(); downloadExcel(response, newFile, excelName); deleteFile(newFile); } catch (Exception e) { logger.error("——————————————————————————數據寫入表格失敗————————————————————————————"); } } }
cell.setCellType(Cell.CELL_TYPE_NUMERIC); cell.setCellValue(((Double) val).doubleValue());
針對這兩句,咱們查看poi的cell.setCellValue()方法的底層是如何實現的。框架
void setCellValue(double var1); void setCellValue(Date var1); void setCellValue(Calendar var1); void setCellValue(RichTextString var1); void setCellValue(String var1); void setCellValue(boolean var1);
在java的八大類型當中,poi支持浮點型的double類型,整型的integer類型。爲何是這兩種類型?這兩種格式是java的默認類型。double能夠轉整型的。
全部,咱們在進行數據導出時,必定要主營待導出對象的數據類型,避免數據沒法導出。spa
/** * Created By zby on 19:24 2019/3/9 * * @param request 客戶端發出的導入請求 * @param sheetDataStartRow 導入的開始行 * @param sheetDataEndCol 導入的結束列 */ public static LinkedHashMap<String, List<JSONObject>> importMultiSheetExcel(HttpServletRequest request, Integer sheetDataStartRow, String sheetDataEndCol) { //建立導入和行和列 LinkedHashMap<Integer, String> sheetDataEndColMap = new LinkedHashMap<>(); LinkedHashMap<Integer, Integer> sheetFirstDataRowMap = new LinkedHashMap(); sheetDataEndColMap.put(0, sheetDataEndCol); sheetFirstDataRowMap.put(0, sheetDataStartRow); LinkedHashMap<String, List<JSONObject>> resMap = new LinkedHashMap<>(); try { MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request; ifNullThrow(multipartRequest, ResultCodeEnum.ILLEGAL_PARAM); MultipartFile file = multipartRequest.getFile("file"); Workbook work = getWorkbook(file.getInputStream(), file.getOriginalFilename()); ifNullThrow(work, ResultCodeEnum.ILLEGAL_PARAM); Sheet sheet = null; Row row = null; Cell cell = null; //遍歷Excel中全部的sheet for (int i = 0; i < work.getNumberOfSheets(); i++) { sheet = work.getSheetAt(i); if (null == sheet) { continue; } String sheetDataEndCo = sheetDataEndColMap.get(i); Integer sheetFirstDataRow = sheetFirstDataRowMap.get(i); sheetDataEndCo = isNotBlank(sheetDataEndCo) ? sheetDataEndCo.toUpperCase() : null; sheetFirstDataRow = isNotNull(sheetFirstDataRow) ? sheetFirstDataRow : 1; List<JSONObject> list = new ArrayList<>(); //遍歷當前sheet中的全部行 for (int j = 0; j <= sheet.getLastRowNum(); j++) { row = sheet.getRow(j); if (row == null) { continue; } if (j >= sheetFirstDataRow.intValue()) { JSONObject json = new JSONObject(); //導入限制最大列索引數爲200,正常根據sheetDataEndCol肯定,最大列索引數限制只用來防止傳入錯誤 for (int k = 0; k < 200; k++) { String colName = CellReference.convertNumToColString(k); cell = row.getCell(k); if (isNotNull(cell)) { if (cell.getCellType() == Cell.CELL_TYPE_STRING) { cell.setCellType(Cell.CELL_TYPE_STRING); json.put(colName, cell.getStringCellValue().trim()); } else if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) { if (HSSFDateUtil.isCellDateFormatted(cell)) { json.put(colName, cell.getDateCellValue()); } else { cell.setCellType(Cell.CELL_TYPE_STRING); json.put(colName, cell.getStringCellValue().trim()); } } } else { json.put(colName, null); } if (colName.equals(sheetDataEndCo)) { break; } } list.add(json); } } resMap.put("sheet" + i, list); } return resMap; } catch (Exception e) { throw new GeneralBizException("Excel導入異常:" + e.getMessage()); } }
由於Excel格式的時間和java的時間不一致,咱們在導入時須要注意Excel的格式。首先判斷當前單元格的數據是否是數字型的,若是是數字型的,在判斷是否是日期類型的,若是是日期類型,再轉爲日期類型。不然,所有是字符型的數據。hibernate
if (cell.getCellType() == Cell.CELL_TYPE_STRING) { cell.setCellType(Cell.CELL_TYPE_STRING); json.put(colName, cell.getStringCellValue().trim()); } else if (cell.getCellType() == Cell.CELL_TYPE_NUMERIC) { if(HSSFDateUtil.isCellDateFormatted(cell)){ json.put(colName, cell.getDateCellValue()); }else { cell.setCellType(Cell.CELL_TYPE_STRING); json.put(colName, cell.getStringCellValue().trim()); } }
這裏爲何不作數值型的判斷,由於,字符串能夠轉爲各中數值型的數據。設計
任何框架的搭建,都須要紮實的基礎,只有基礎足夠強大,再加上靈活的設計思想,就可以解決不少事情。excel