Apache POI 4.1.1跨工作簿复制带样式工作表时单元格填充黑色问题
问题描述
使用Apache POI 4.1.1实现跨工作簿复制工作表,要求保留单元格颜色、合并单元格等样式。现有代码可完成基础内容复制,但原本带填充色的单元格复制后全部变为黑色,多次排查未找到根因。
现有完整实现代码如下:
public class SheetUtil { private static void removeRows(Sheet destSheet) { if (null != destSheet) { for (int i = destSheet.getFirstRowNum(); i <= destSheet.getLastRowNum(); i++) { Row row = destSheet.getRow(i); if (null != row) { destSheet.removeRow(row); } } } } private static void addRows(Sheet destSheet, int totalRowCount) { if (null != destSheet) { for (int i = 0; i <= totalRowCount; i++) { destSheet.createRow(i); } } } static void copyMergedRegion(Sheet srcSheet, Sheet destSheet) { for (int i = 0; i < srcSheet.getNumMergedRegions(); i++) { destSheet.addMergedRegion(srcSheet.getMergedRegion(i)); } } private static void copyCell(Cell srcCell, Cell destCell, Map<Integer, CellStyle> styleMap) { if (styleMap != null) { int srcCellHashCode = srcCell.getCellStyle().hashCode(); CellStyle newCellStyle = styleMap.get(srcCellHashCode); if (null == newCellStyle) { newCellStyle = destCell.getSheet().getWorkbook().createCellStyle(); newCellStyle.setAlignment(srcCell.getCellStyle().getAlignment()); newCellStyle.setBorderBottom(srcCell.getCellStyle().getBorderBottom()); newCellStyle.setBorderLeft(srcCell.getCellStyle().getBorderLeft()); newCellStyle.setBorderRight(srcCell.getCellStyle().getBorderRight()); newCellStyle.setBorderTop(srcCell.getCellStyle().getBorderTop()); newCellStyle.setDataFormat(srcCell.getCellStyle().getDataFormat()); newCellStyle.setFillBackgroundColor(srcCell.getCellStyle().getFillBackgroundColor()); newCellStyle.setFillForegroundColor(srcCell.getCellStyle().getFillForegroundColor()); newCellStyle.setFillPattern(srcCell.getCellStyle().getFillPattern()); newCellStyle.setVerticalAlignment(srcCell.getCellStyle().getVerticalAlignment()); newCellStyle.setWrapText(srcCell.getCellStyle().getWrapText()); styleMap.put(srcCellHashCode, newCellStyle); } destCell.setCellStyle(newCellStyle); } if (srcCell.getCellType() == CellType.BLANK) { destCell.setBlank(); } else if (srcCell.getCellType() == CellType.STRING) { destCell.setCellValue(srcCell.getStringCellValue()); } else if (srcCell.getCellType() == CellType.NUMERIC) { destCell.setCellValue(srcCell.getNumericCellValue()); } else if (srcCell.getCellType() == CellType.BOOLEAN) { destCell.setCellValue(srcCell.getBooleanCellValue()); } else if (srcCell.getCellType() == CellType.FORMULA) { destCell.setCellFormula(srcCell.getCellFormula()); } else if (srcCell.getCellType() == CellType.ERROR) { destCell.setCellErrorValue(srcCell.getErrorCellValue()); } } private static void copyRow(Row srcRow, Row destRow, Map<Integer, CellStyle> styleMap) { destRow.setHeight(srcRow.getHeight()); for (int j = srcRow.getFirstCellNum(); j <= srcRow.getLastCellNum(); j++) { Cell srcCell = srcRow.getCell(j); if (srcCell != null) { Cell destCell = destRow.createCell(j); copyCell(srcCell, destCell, styleMap); } } } /** * * Copy a sheet from one workbook to another workbook. * * @param srcSheet * @param destSheet */ public static void copySheet(Sheet srcSheet, Sheet destSheet) { removeRows(destSheet); addRows(destSheet, srcSheet.getLastRowNum()); copyMergedRegion(srcSheet, destSheet); Map<Integer, CellStyle> styleMap = new HashMap<Integer, CellStyle>(); for (int i = srcSheet.getFirstRowNum(); i <= srcSheet.getLastRowNum(); i++) { Row srcRow = srcSheet.getRow(i); if (null == srcRow) { destSheet.createRow(i); } else { Row destRow = destSheet.createRow(i); copyRow(srcRow, destRow, styleMap); } } } public void test1() { try { System.out.println(" test1() : " + new Date(System.currentTimeMillis())); File templateFile = new File("C:/poiTest/Template_V2.xlsx"); InputStream inputStream = new FileInputStream(templateFile); Workbook merWorkBook = WorkbookFactory.create(inputStream); inputStream.close(); Sheet destPdrSheet = merWorkBook.getSheet("PDR"); File pdrFile = new File("C:/poiTest/P23163.xlsx"); InputStream pdrInputStream = new FileInputStream(pdrFile); Workbook pdrWorkBook = WorkbookFactory.create(pdrInputStream); pdrInputStream.close(); Sheet srcPdrSheet = pdrWorkBook.getSheetAt(0); SheetUtil.copySheet(srcPdrSheet, destPdrSheet); ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); merWorkBook.setForceFormulaRecalculation(true); merWorkBook.write(byteArrayOutputStream); FileOutputStream resultFile = new FileOutputStream(new File("C:/poiTest/outputXLSX1.xlsx")); byteArrayOutputStream.writeTo(resultFile); System.out.println(" test1() : " + new Date(System.currentTimeMillis())); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { SheetUtil obj = new SheetUtil(); obj.test1(); } }
根因分析
填充色变黑是因为手动复制样式的逻辑存在缺陷:
- 调用
getFillForegroundColor()、getFillBackgroundColor()获取到的是颜色在源工作簿调色板中的索引值,这个索引仅在源工作簿内有效。跨工作簿直接使用该索引时,目标工作簿对应索引位置存储的默认颜色为黑色,最终导致填充色显示异常。 - 手动枚举样式属性的方式本身存在漏项风险,现有代码未复制字体、边框颜色、文本旋转、缩进等大量样式属性,除了填充色外,其他样式也可能出现错乱。
修复方案
优先使用POI内置的样式克隆方法,无需手动逐属性复制,可自动处理跨工作簿的颜色、字体等资源映射:
将copyCell方法中手动创建CellStyle并逐属性赋值的逻辑,替换为cloneStyleFrom调用即可:
if (styleMap != null) { int srcCellHashCode = srcCell.getCellStyle().hashCode(); CellStyle newCellStyle = styleMap.get(srcCellHashCode); if (null == newCellStyle) { newCellStyle = destCell.getSheet().getWorkbook().createCellStyle(); // 自动克隆所有样式属性,包含颜色、字体、边框等全量配置 newCellStyle.cloneStyleFrom(srcCell.getCellStyle()); styleMap.put(srcCellHashCode, newCellStyle); } destCell.setCellStyle(newCellStyle); }
如果必须手动实现样式复制,需要额外处理颜色的跨工作簿转换:提取源颜色的RGB值,在目标工作簿中创建对应颜色对象后再赋值,同时补全字体、边框颜色等其余样式属性的复制逻辑。该方案需要同时兼容XLS、XLSX两种格式的颜色规则,维护成本高,不推荐使用。
内容的提问来源于stack exchange,提问作者user110009
相关产品推荐
相关产品推荐

