package com.engine.salary.util.excel; import cn.hutool.core.util.StrUtil; import com.engine.salary.util.FileUtils; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellUtil; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.regex.Pattern; public class ExcelFillUtils { // public static final String FILL_EXPRESSION_REGEX = "\\{\\.\\w+\\}"; private static final String FILL_EXPRESSION_REGEX = "\\{\\.[\\p{L}\\p{M}\\S]+\\}"; /** * 给定模板,指定某个页签,将数据填充到模板中的指定页签,并将数据导入到指定文件上。 * * @param template 模板文件地址 * @param sheetIndex 页签下标 * @param data 待填充的数据,数据格式如下 * [ * {"colName1":v1 ,"colName2":v2...}, * {"colName1":v1 ,"colName2":v2...} * ,... * ] * @return 新生成的副本文件 */ public static XSSFWorkbook fillOneSheet(String template, Integer sheetIndex, List> data) { try { XSSFWorkbook workbook = new XSSFWorkbook(new FileInputStream(template)); fill(workbook, sheetIndex, data);//填充数据 return workbook; } catch (IOException e) { throw new RuntimeException(e); } } /** * 给定模板,指定某个页签,将数据填充到模板中的指定页签,并将数据导入到指定文件上。 * * @param template 模板文件流 * @param sheetIndex 页签名称 * @param data 待填充的数据,数据格式如下 * [ * {"colName1":v1 ,"colName2":v2...}, * {"colName1":v1 ,"colName2":v2...} * ,... * ] * @return 新生成的副本文件 */ public static XSSFWorkbook fillOneSheet(InputStream template, Integer sheetIndex, List> data) { try{ XSSFWorkbook workbook = new XSSFWorkbook(template); fill(workbook, sheetIndex, data);//填充数据 return workbook; } catch (IOException e) { throw new RuntimeException(e); } } /** * 给定模板,将数据填充到模板中的多个页签,并在模板所在目录生成新的副本文件。 * * @param template 模板文件地址 * @param datas 待填充的数据集,数据格式如下 * { * "SheetName1":[ * {"colName1":v1 ,"colName2":v2...}, * {"colName1":v1 ,"colName2":v2...} * ,... * ], * "SheetName2":[ * {"colName1":v1 ,"colName2":v2...} * {"colName1":v1 ,"colName2":v2...} * ,... * ], * ... * } */ public static String fillMultipleSheet(String template, Map>> datas) { return fillMultipleSheet(template, FileUtils.getAvailableFullName(template), datas); } /** * 给定模板,将数据填充到模板中的多个页签,并将数据导入到指定文件上。 * * @param template 模板文件地址 * @param datas 待填充的数据集,数据格式如下 * { * "SheetName1":[ * {"colName1":v1 ,"colName2":v2...}, * {"colName1":v1 ,"colName2":v2...} * ,... * ], * "SheetName2":[ * {"colName1":v1 ,"colName2":v2...} * {"colName1":v1 ,"colName2":v2...} * ,... * ], * ... * } */ public static String fillMultipleSheet(String template, String outputFile, Map>> datas) { try (Workbook workbook = new XSSFWorkbook(new FileInputStream(template))) { datas.forEach((sheetIndex, data) -> fill(workbook, sheetIndex, data));//填充数据 refreshFormula(workbook);//刷新公式 try (FileOutputStream outputStream = new FileOutputStream(outputFile)) { workbook.write(outputStream); } catch (Exception e) { throw new RuntimeException(e); } } catch (IOException e) { throw new RuntimeException(e); } return outputFile; } private static Workbook fill(Workbook workbook, int sheetIndex, List> data) { Sheet sheet = workbook.getSheetAt(sheetIndex); if (sheet == null) { throw new RuntimeException(String.format("sheet [%s] does not exist.", sheetIndex)); } //找到所有的表达式单元格 Map expressionCellMap = new HashMap<>(); for (int i = 0; i < sheet.getPhysicalNumberOfRows(); i++) { Row row = sheet.getRow(i); for (int j = 0; j < row.getPhysicalNumberOfCells(); j++) { Cell cell = row.getCell(j); if (!Objects.isNull(cell) && isFillExpression(cell.getStringCellValue())) {//判断该单元格是否是填充公式 expressionCellMap.put(getColNameFromEx(cell.getStringCellValue()), cell); } } } //填充数据 for (int i = 0; i < data.size(); i++) { Map dataRow = data.get(i); for (Map.Entry entry : dataRow.entrySet()) { String colName = entry.getKey(); Object value = entry.getValue(); if (expressionCellMap.containsKey(colName)) { Cell cell = expressionCellMap.get(colName);//公式所在的单元格 int rowID = cell.getRowIndex() + i; int colId = cell.getColumnIndex(); Row fillRow = sheet.getRow(rowID); fillRow = Objects.isNull(fillRow) ? sheet.createRow(rowID) : fillRow; Cell fillCell = fillRow.getCell(colId); //创建的新单元格需要复制公式单元格的格式 fillCell = Objects.isNull(fillCell) ? CellUtil.createCell(fillRow, colId, "", cell.getCellStyle()) : fillCell; if (value instanceof String) { fillCell.setCellValue(String.valueOf(value)); } else if (value instanceof Number) { fillCell.setCellValue(((Number) value).doubleValue()); } else { throw new RuntimeException(String.format("Unsupported data type [%s].", value.getClass().toString())); } } } } return workbook; } private static void refreshFormula(Workbook workbook) { FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); evaluator.evaluateAll(); } private static boolean isFillExpression(String ex) { if (StrUtil.isBlank(ex)) { return false; } ; return Pattern.matches(FILL_EXPRESSION_REGEX, ex); } private static String getColNameFromEx(String ex) { if (!isFillExpression(ex)) throw new RuntimeException("Illegal expression " + ex); return ex.substring(2, ex.length() - 1); } }