Excel填值工具
阿新 • • 發佈:2019-01-12
1.背景
最近遇到需要匯出Excel報告,但是模板上有一些格式等等,外加模板是固定的,所以沒有打算使用freemaker之類的工具匯出模板.採取的方式為在Excel中填上佔位符然後將Excel模板的值替換的方式.
2.工具
工具方面使用poi來操作Excel,首先讀取Excel表格然後讀取表格的佔位符,將佔位符的值進行替換.
3.程式碼
3.1 poi依賴引入
<!-- poi引入 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.17</version> </dependency>
3.2工具類
import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.log4j.Logger; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.usermodel.XSSFWorkbook; /** * excel工具類 * @author sly * @time 2019年1月12日 */ public class ExcelUtils { private static Logger logger = Logger.getLogger(ExcelUtils.class); public static final String EXCEL_XLS = "xls"; public static final String EXCEL_XLSX = "xlsx"; /** * 測試類 * @param args * @author sly * @time 2019年1月12日 */ public static void main(String[] args) { //組裝測試資料 Map<String, String> params = new HashMap<>(16); params.put("theme_1", "主題一"); params.put("theme_2", "主題二"); params.put("theme_3", "主題三"); params.put("theme_5", "主題五"); params.put("theme_6", "主題六"); Workbook wb = null; InputStream inputStream = null; File file = new File("D:\\測試\\Excel值替換模板.xlsx"); OutputStream outputStream = null; try { if(file.getName().endsWith(EXCEL_XLS)) { inputStream = new FileInputStream(file); wb = new HSSFWorkbook(inputStream); }else if(file.getName().endsWith(EXCEL_XLSX)) { inputStream = new FileInputStream(file); wb = new XSSFWorkbook(inputStream); }else { System.out.println("不是Excel"); return ; } //獲取位元組陣列 Excel寬度為9 byte[] bytes = getExcelBinary(wb, 9, params); outputStream = new FileOutputStream(new File("D:\\測試\\Excel值替換結果.xlsx")); outputStream.write(bytes); } catch (Exception e) { logger.error(ExceptionUtils.getStackTrace(e)); throw new RuntimeException(ExceptionUtils.getStackTrace(e)); } finally { if(outputStream != null) { try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if(wb != null) { try { wb.close(); } catch (IOException e) { e.printStackTrace(); } } if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * Excel值替換 * @param wb * @param width * @param params * @return * @author sly * @time 2019年1月12日 */ public static byte[] getExcelBinary(Workbook wb,int width,Map<String, String> params) { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); try { Sheet sheet = wb.getSheetAt(0); int numMergedRegions = sheet.getNumMergedRegions(); List<Cell> cells = new ArrayList<>(); //合併的單元格 List<CellRangeAddress> caList = new ArrayList<CellRangeAddress>(); for (int i = 0; i < numMergedRegions; i++) { CellRangeAddress ca = sheet.getMergedRegion(i); caList.add(ca); int firstRow = ca.getFirstRow(); Row row = sheet.getRow(firstRow); Cell cell = row.getCell(ca.getFirstColumn()); if(cell != null) { cells.add(cell); } } //獲取高度Excel模板 int height = sheet.getLastRowNum() + 1; //獲取普通單元格 for (int i = 0; i < height; i++) { Row row = sheet.getRow(i); for (int j = 0; j < width; j++) { if (!isCombineCell(caList, j, i)) { Cell cell = row.getCell(j); if (StringUtils.isNotBlank(getCellValue(cell))) { cells.add(cell); } } } } ////替換佔位符的值 for (int i = 0; i < cells.size(); i++) { Cell cell = cells.get(i); if(cell != null) { String cellValue = getCellValue(cell); List<String> placeHolders = getAllPlaceHolder(cellValue); for (int j = 0; j < placeHolders.size(); j++) { String placeHolder = placeHolders.get(j); String value = params.get(placeHolder); if(value != null) { cellValue = cellValue.replaceAll("\\$\\{" + placeHolder + "\\}", value); }else { cellValue = cellValue.replaceAll("\\$\\{" + placeHolder + "\\}", ""); } } setCellValue(cell, cellValue); cellValue = getCellValue(cell); } } wb.write(byteArrayOutputStream); @SuppressWarnings("null") byte[] byteArray = byteArrayOutputStream.toByteArray(); return byteArray; } catch (Exception e) { logger.error(ExceptionUtils.getStackTrace(e)); throw new RuntimeException("Excel替換值失敗:" + ExceptionUtils.getStackTrace(e)); } finally { if(wb != null) { try { wb.close(); } catch (IOException e) { e.printStackTrace(); } } if(byteArrayOutputStream != null) { try { byteArrayOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 獲取單元格值 * @param cell * @return * @author sly * @time 2018年11月30日 */ public static String getCellValue(Cell cell){ if(cell == null) return ""; if(cell.getCellTypeEnum() == CellType.STRING){ return cell.getStringCellValue(); }else if(cell.getCellTypeEnum() == CellType.BOOLEAN){ return String.valueOf(cell.getBooleanCellValue()); }else if(cell.getCellTypeEnum() == CellType.FORMULA){ return cell.getCellFormula() ; }else if(cell.getCellTypeEnum() == CellType.NUMERIC){ return String.valueOf(cell.getNumericCellValue()); } return ""; } /** * 設定單元格值 * @param cell * @param value * @author sly * @time 2018年11月30日 */ public static void setCellValue(Cell cell,String value){ cell.setCellValue(value); } /** * 替換單元格值 * @param value * @param params * @return * @author sly * @time 2018年11月30日 */ public static String replaceValue(String value,Map<String, String> params) { if(StringUtils.isNotBlank(value)) { return value; } return value; } /** * 判斷該格子是否為合併單元格 * @param caList * @param x * @param y * @return * @author sly * @time 2018年11月30日 */ public static boolean isCombineCell(List<CellRangeAddress> caList,int x,int y) { int cax1 = 0; int cax2 = 0; int cay1 = 0; int cay2 = 0; for (int i = 0; i < caList.size(); i++) { CellRangeAddress ca = caList.get(i); cax1 = ca.getFirstColumn(); cax2 = ca.getLastColumn(); cay1 = ca.getFirstRow(); cay2 = ca.getLastRow(); if(x >= cax1 && x <= cax2) { if(y >= cay1 && y <= cay2) { return true; } } } return false; } /** * 獲取佔位符 * @param str * @return * @author sly * @time 2018年11月30日 */ public static List<String> getAllPlaceHolder(String str) { List<String> list = new ArrayList<>(); if(StringUtils.isNotBlank(str)) { Pattern pattern = Pattern.compile("\\$\\{([^}]*)\\}"); Matcher matcher = pattern.matcher(str); while (matcher.find()) { list.add(matcher.group(1)); } } return list; } }
4.測試Excel檔案
替換前模板
替換後新生成檔案
經過對比可以看到所有佔位符已經替換完成,${theme_4}因為沒有值所以被替換為空值
5.小結
該工具類應用範圍比較小,只能用於模板固定的Excel,但是好處是可以完整保留Excel樣式例如:
替換前:
替換後:
可見字型顏色和背景都保留了下來,當然其它型別的格式沒有進行進一步測試,不過這裡也只是提供一種思路.算是一種特殊情況下偷懶的做法.