You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 23:15:11