如何使源xlsx文件复制的样式与目标xlsx文件的样式一致?

问题描述 投票:0回答:1

erryone.这是我遇到的问题。 使用 POI-OOXML 4.1.2 将 xlsx 中的多个工作表页面拆分为仅包含一个工作表页面的多个 xlsx 文件会导致复制时样式不一致。如何解决这个问题呢? 这是源 xlsx 文件: source pic 这是目标 xlsx 文件: target pic 两张图的区别在于填充颜色不同。源图片的预期填充颜色是什么。源图片的第一行和第二行的填充颜色是 RGB HEX F4B7BE,但目标图片的 RGB HEX 是 C6E0B4。他们是不同的。 这是我复制的 cellStyle 代码:

       ...
            targetStyle = (XSSFCellStyle) targetWorkbook.createCellStyle();

            targetStyle.cloneStyleFrom(sourceStyle);

            copyFont(((XSSFCellStyle) sourceStyle).getFont(), ((XSSFCellStyle) targetStyle).getFont(), targetStyle);

预期的填充颜色是什么是源图片。 我尝试使用以下代码复制样式:

            targetStyle.cloneStyleFrom(sourceStyle);

            XSSFColor sourceFillForegroundXSSFColor = ((XSSFCellStyle) sourceStyle).getFillForegroundXSSFColor();
            XSSFColor sourceFillBackgroundXSSFColor = ((XSSFCellStyle) sourceStyle).getFillBackgroundXSSFColor();
            ((XSSFCellStyle) targetStyle).setFillForegroundColor(sourceFillForegroundXSSFColor);
            ((XSSFCellStyle) targetStyle).setFillBackgroundColor(sourceFillBackgroundXSSFColor);
            targetStyle.setFillPattern(sourceStyle.getFillPattern());

没成功。 这是我的所有代码如下: `

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.EncryptedDocumentException;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFFont;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
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;

public class ExcelSplitterUtil {

private final static Map<Integer, Map<Integer, CellStyle>> styleCacheLocal = new HashMap<>();

private final static String excelFileType = ".xlsx";

public static void main(String[] args) {
String inputFilePath = "C:\\Users\\15206\\Desktop\\excel_sheet\\test3\\xlsx文件样式测试1.xlsx";
String outputFolderPath = "C:\\Users\\15206\\Desktop\\excel_sheet\\test3\\target\\";
excelSplitter(inputFilePath, outputFolderPath);
}

public static void excelSplitter(String inputFilePath, String outputFolderPath) {


try (FileInputStream fileInputStream = new FileInputStream(inputFilePath)) {
    Workbook sourceWorkbook;
    if (inputFilePath.endsWith(".xlsx")) {
        sourceWorkbook = new XSSFWorkbook(fileInputStream);
    } else if (inputFilePath.endsWith(".xls")) {
        sourceWorkbook = new HSSFWorkbook(fileInputStream);
    } else {
        return;
    }

    for (int sheetIndex = 0; sheetIndex < sourceWorkbook.getNumberOfSheets(); sheetIndex++) {
        Workbook targetWorkbook = null;
        String sheetName = sourceWorkbook.getSheetAt(sheetIndex).getSheetName();

        if (StringUtils.isNoneBlank(excelFileType)) {
            if (excelFileType.equals(".xlsx")) {
                targetWorkbook = new XSSFWorkbook();
            } else if (excelFileType.equals(".xls")) {
                targetWorkbook = new HSSFWorkbook();
            } else {
                //log.error("Unsupported Excel file format: {}", excelFileType);
                break;
            }
        } else {
            //throw new BizRuntimeException("ExcelFileType cannot be empty!");
        }
        //}

        Sheet sourceSheet = sourceWorkbook.getSheetAt(sheetIndex);
        Sheet targetSheet = targetWorkbook.createSheet(sourceSheet.getSheetName());

        copySheet(sourceSheet, targetSheet, sourceWorkbook, targetWorkbook, excelFileType);

        String outputFilePath = outputFolderPath + sheetName + excelFileType;

        List<String> uploadPaths = new ArrayList<>();
        uploadPaths.add(outputFilePath);

        if (!new File(outputFilePath).exists()) {
            String tempDir = outputFilePath.substring(0, outputFilePath.lastIndexOf(File.separator));
            new File(tempDir).mkdirs();
        }

        try (FileOutputStream fileOutputStream = new FileOutputStream(outputFilePath)) {
            targetWorkbook.write(fileOutputStream);
        }
        targetWorkbook.close();
    }

    sourceWorkbook.close();
} catch (IOException e) {

} catch (EncryptedDocumentException e) {

}
}

private static void copySheet(Sheet sourceSheet, Sheet targetSheet, Workbook sourceWorkbook, Workbook targetWorkbook, String excelFileType) {

int totalRowNum = sourceSheet.getLastRowNum() - sourceSheet.getFirstRowNum() + 1;
System.out.println("totalRowNum--------------------" + totalRowNum);

for (int rowIndex = 0; rowIndex < totalRowNum; rowIndex++) {
    Row sourceRow = sourceSheet.getRow(rowIndex);
    Row targetRow = targetSheet.createRow(rowIndex);

    if (sourceRow != null) {
        // 设置行宽
        float sourceRowHeightInPoints = sourceRow.getHeightInPoints();
        targetRow.setHeightInPoints(sourceRowHeightInPoints);

        int cellNum = sourceRow.getLastCellNum();
        System.out.println("cellNum-----------{}----" + cellNum);
        for (int colIndex = 0; colIndex < sourceRow.getLastCellNum(); colIndex++) {
            Cell sourceCell = sourceRow.getCell(colIndex);
            Cell targetCell = targetRow.createCell(colIndex);

            if (sourceCell != null) {
                // 处理不同数据类型的赋值
                setCellValue(targetCell, sourceCell);

                // 设置列宽
                int columnWidth = sourceSheet.getColumnWidth(colIndex);
                // 从第colIndex列开始,该列的宽度是 columnWidth
                targetSheet.setColumnWidth(colIndex, columnWidth);

                //复制样式
                copyCellStyle(sourceCell, targetCell, targetSheet, sourceWorkbook, targetWorkbook, excelFileType);
            }

        }
    }
}
}

private static void copyCellStyle(Cell sourceCell, Cell targetCell, Sheet targetSheet,
                              Workbook sourceWorkbook, Workbook targetWorkbook, String excelFileType) {
CellStyle sourceStyle = sourceCell.getCellStyle();
CellStyle targetStyle = getOrCreateTargetStyle(sourceStyle, sourceWorkbook, targetWorkbook, excelFileType);

// 将新样式应用于目标单元格
targetCell.setCellStyle(targetStyle);

// 合并单元格信息
CellRangeAddress mergedRegion = findMergedRegion(sourceCell, sourceCell.getSheet());
if (mergedRegion != null && !isMergedRegionOverlapping(mergedRegion, targetSheet)) {
    targetCell.getSheet().addMergedRegion(mergedRegion);
}
}

private static CellStyle getOrCreateTargetStyle(CellStyle sourceStyle, Workbook sourceWorkbook, Workbook targetWorkbook, String excelFileType) {
int sourceStyleIndex = sourceStyle.getIndex();

if (styleCacheLocal.containsKey(sourceStyleIndex)) {
    Map<Integer, CellStyle> workbookStyles = styleCacheLocal.get(sourceStyleIndex);
    return workbookStyles.computeIfAbsent(System.identityHashCode(targetWorkbook), key -> createAndCacheStyle(sourceStyle, sourceWorkbook, targetWorkbook, excelFileType));
} else {
    return createAndCacheStyle(sourceStyle, sourceWorkbook, targetWorkbook, excelFileType);
}
}

private static CellStyle createAndCacheStyle(CellStyle sourceStyle, Workbook sourceWorkbook, Workbook targetWorkbook, String excelFileType) {
CellStyle targetStyle = null;
if (excelFileType.equals(".xlsx") && sourceStyle instanceof XSSFCellStyle) {
    // 源excel 是 xlsx, 目标是xlsx
    targetStyle = (XSSFCellStyle) targetWorkbook.createCellStyle();
    // 复制基本的样式属性
    targetStyle.cloneStyleFrom(sourceStyle);

    //复制字体
    copyFont(((XSSFCellStyle) sourceStyle).getFont(), ((XSSFCellStyle) targetStyle).getFont(), targetStyle);
} else if (excelFileType.equals(".xlsx") && sourceStyle instanceof HSSFCellStyle) {
    // 源excel 是 xls, 目标是xlsx
    targetStyle = (XSSFCellStyle) targetWorkbook.createCellStyle();
    // 复制其他样式属性
    manualCloneStyle(targetStyle, sourceStyle);

    //复制字体
    HSSFFont sourceFont = (HSSFFont) sourceWorkbook.getFontAt(sourceStyle.getFontIndexAsInt());
    copyFont(sourceFont, ((XSSFCellStyle) targetStyle).getFont(), targetStyle);
} else if (excelFileType.equals(".xls") && sourceStyle instanceof XSSFCellStyle) {
    // 源excel 是 xlsx, 目标是xls
    targetStyle = (HSSFCellStyle) targetWorkbook.createCellStyle();
    // 手动复制不兼容样式
    manualCloneStyle(targetStyle, sourceStyle);

    //复制字体
    copyFont(((XSSFCellStyle) sourceStyle).getFont(), ((HSSFCellStyle) targetStyle).getFont(targetWorkbook), targetStyle);

} else if (excelFileType.equals(".xls") && sourceStyle instanceof HSSFCellStyle) {
    // 源excel 是 xls, 目标是xls
    targetStyle = (HSSFCellStyle) targetWorkbook.createCellStyle();
    // 复制基本的样式属性
    targetStyle.cloneStyleFrom(sourceStyle);

    //复制字体
    copyFont(((HSSFCellStyle) sourceStyle).getFont(sourceWorkbook), ((HSSFCellStyle) targetStyle).getFont(targetWorkbook), targetStyle);
} else {
    System.out.println("文件类型excelFileType不匹配");
    return null;
}

Map<Integer, CellStyle> workbookStyles = new HashMap<>();
workbookStyles.put(System.identityHashCode(targetWorkbook), targetStyle);
styleCacheLocal.put(Integer.valueOf(sourceStyle.getIndex()), workbookStyles);

return targetStyle;
}

private static void manualCloneStyle(CellStyle targetStyle, CellStyle sourceStyle) {
targetStyle.setAlignment(sourceStyle.getAlignment());
targetStyle.setVerticalAlignment(sourceStyle.getVerticalAlignment());
targetStyle.setWrapText(sourceStyle.getWrapText());
targetStyle.setDataFormat(sourceStyle.getDataFormat());
targetStyle.setFillForegroundColor(sourceStyle.getFillForegroundColor());
targetStyle.setFillBackgroundColor(sourceStyle.getFillBackgroundColor());
targetStyle.setFillPattern(sourceStyle.getFillPattern());
targetStyle.setHidden(sourceStyle.getHidden());
targetStyle.setIndention(sourceStyle.getIndention());
targetStyle.setLocked(sourceStyle.getLocked());
targetStyle.setRotation(sourceStyle.getRotation());
targetStyle.setBorderBottom(sourceStyle.getBorderBottom());
targetStyle.setBorderLeft(sourceStyle.getBorderLeft());
targetStyle.setBorderRight(sourceStyle.getBorderRight());
targetStyle.setBorderTop(sourceStyle.getBorderTop());
targetStyle.setTopBorderColor(sourceStyle.getTopBorderColor());
targetStyle.setBottomBorderColor(sourceStyle.getBottomBorderColor());
targetStyle.setLeftBorderColor(sourceStyle.getLeftBorderColor());
targetStyle.setQuotePrefixed(sourceStyle.getQuotePrefixed());
targetStyle.setShrinkToFit(sourceStyle.getShrinkToFit());
}

private static void copyFont(Font sourceFont, Font targetFont, CellStyle cellStyle) {
targetFont.setFontName(sourceFont.getFontName());
targetFont.setFontHeightInPoints(sourceFont.getFontHeightInPoints());
targetFont.setBold(sourceFont.getBold());
targetFont.setItalic(sourceFont.getItalic());

if (targetFont instanceof XSSFFont) {
    if (sourceFont instanceof XSSFFont) {
        ((XSSFFont) targetFont).setColor(((XSSFFont) sourceFont).getXSSFColor());
    } else if (sourceFont instanceof HSSFFont) {
        ((XSSFFont) targetFont).setColor(sourceFont.getColor());
    }
} else {
    if (sourceFont instanceof XSSFFont) {
        ((HSSFFont) targetFont).setColor(sourceFont.getColor());
    } else if (sourceFont instanceof HSSFFont) {
        ((HSSFFont) targetFont).setColor(sourceFont.getColor());
    }
}
// 设置新字体到新样式
cellStyle.setFont(targetFont);
}

private static void setCellValue(Cell targetCell, Cell sourceCell) {
switch (sourceCell.getCellType()) {
    case STRING:
        targetCell.setCellValue(sourceCell.getStringCellValue());
        break;
    case NUMERIC:
        if (DateUtil.isCellDateFormatted(sourceCell)) {
            targetCell.setCellValue(sourceCell.getDateCellValue());
        } else {
            targetCell.setCellValue(sourceCell.getNumericCellValue());
        }
        break;
    case BOOLEAN:
        targetCell.setCellValue(sourceCell.getBooleanCellValue());
        break;
    case FORMULA:
        targetCell.setCellFormula(sourceCell.getCellFormula());
        break;
    default:
        targetCell.setCellValue(sourceCell.toString());
        break;
}
}

private static boolean isMergedRegionOverlapping(CellRangeAddress newMergedRegion, Sheet targetSheet) {
for (int i = 0; i < targetSheet.getNumMergedRegions(); i++) {
    CellRangeAddress existingMergedRegion = targetSheet.getMergedRegion(i);
    if (existingMergedRegion.intersects(newMergedRegion)) {
        return true;
    }
}
return false;
}

private static CellRangeAddress findMergedRegion(Cell cell, Sheet sourceSheet) {
for (int i = 0; i < sourceSheet.getNumMergedRegions(); i++) {
    CellRangeAddress merged = sourceSheet.getMergedRegion(i);
    if (merged.isInRange(cell.getRowIndex(), cell.getColumnIndex())) {
        return merged;
    }
}
return null;
}
}

and here is my pom.xml as follow :

<properties>
    <maven.compiler.source>8</maven.compiler.source>
    <maven.compiler.target>8</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

<dependencies>
    <!-- excel tool -->
    <!--xls(03)-->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>4.1.2</version>
    </dependency>
    <!--xlsx(07)-->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>4.1.2</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
    </dependency>
    <dependency>
        <groupId>joda-time</groupId>
        <artifactId>joda-time</artifactId>
        <version>2.12.1</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>cn.hutool</groupId>
        <artifactId>hutool-all</artifactId>
        <version>4.5.1</version>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.7</version>
    </dependency>

</dependencies>

` 刚才我尝试在Excel中设置样式并保存为源文件,然后将文件交给程序来分割并复制样式。结果是目标文件的风格与源文件的风格不一致。我应该如何调整它以使其相同?这是我的源文件和目标文件的屏幕截图。 source srctarget src

我感觉这是一个 POI bug!

java excel apache-poi
1个回答
0
投票

正如我所说,颜色差异对我来说是无法重现的。至少对于 Office Open XML 格式

*.xlsx
以及使用同一应用程序查看所有内容时不适用。

在不同的应用程序中查看时,差异可能是由于填充颜色是主题颜色,并且一个电子表格应用程序对主题颜色的解释与另一个不同。

但是您的大量代码中可能存在一些问题。或者使用的旧 Apache POI 版本 4.1.2 存在错误。

使用当前的 Apache POI 5.2.5,可以使用以下小代码实现相同的功能:

import java.io.FileInputStream;
import java.io.FileOutputStream;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import org.apache.poi.ss.util.CellUtil;

public class ExcelSplitterUtil  {

 public static void excelSplitter(String inputFilePath, String outputFolderPath) {
  try ( Workbook sourceWorkbook = WorkbookFactory.create(new FileInputStream(inputFilePath)); ) {

   for (Sheet sourceSheet : sourceWorkbook) {

    String sourceSheetName = sourceSheet.getSheetName();

    Workbook targetWorkbook = null;
    String suffix = "";
    if (sourceWorkbook instanceof XSSFWorkbook) {
     targetWorkbook = new XSSFWorkbook();
     suffix = ".xlsx";  
    } else if (sourceWorkbook instanceof HSSFWorkbook) {
     targetWorkbook = new HSSFWorkbook();          
     suffix = ".xls";   
    }

    Sheet targetSheet = targetWorkbook.createSheet(sourceSheetName);

    CellCopyPolicy cellCopyPolicy= new CellCopyPolicy();
    CellCopyContext cellCopyContext = new CellCopyContext();
  
    for (Row sourceRow : sourceSheet) {
     Row targetRow = targetSheet.createRow(sourceRow.getRowNum());
     for (Cell sourceCell : sourceRow) {
      Cell targetCell =  targetRow.createCell(sourceCell.getColumnIndex());
      CellUtil.copyCell(sourceCell, targetCell, cellCopyPolicy, cellCopyContext);
     }
    }

    String outputFilePath = outputFolderPath + sourceSheetName + suffix;
   
    try ( FileOutputStream fileOutputStream = new FileOutputStream(outputFilePath) ) {
     targetWorkbook.write(fileOutputStream);
    }
    targetWorkbook.close();
   }
  } catch (Exception ex) {
   ex.printStackTrace();
  }
 }
  
 public static void main(String[] args) {
  //String inputFilePath = "./source.xls"; //file must exist
  String inputFilePath = "./source.xlsx"; //file must exist
  String outputFolderPath = "./target/"; //path must exist
  excelSplitter(inputFilePath, outputFolderPath);
 }
}

解决方案是使用 CellUtil.copyCell ,其内部功能与您的大量代码相同。也许升级到 Apache POI 5.2.5 后尝试一下?

使用二进制

*.xls
格式 (
HSSF
),使用高于 2007 的 Excel 版本创建
source.xlsx
时会出现颜色差异,并且在保存时会引发兼容性检查警告。这是因为 Apache POI 在引入兼容性检查之前以与旧 Excel 版本相对应的格式创建
*.xls
文件。

© www.soinside.com 2019 - 2024. All rights reserved.