背景
在日常工作中,我们经常需要将多个Excel文件合并成一个,同时保留原有的样式和格式。Apache POI是一个流行的Java库,用于读取和写入Microsoft Office格式的文件,包括Excel。然而,仅仅使用Apache POI的基本功能进行合并操作往往会导致样式的丢失,这对于需要保留格式信息的场景来说是不够的。常见的应用场景如钉钉的打卡数据只能一个一个的表格导出,接下来的文章就恰好可以运用到这样的场景中。
本文将探讨如何在合并Excel文件时保留原表格的样式,并提供一个改进的代码示例,以便更好地处理这个问题。
问题描述
在合并多个Excel文件时,常见的挑战之一是保留每个文件中的单元格样式。这些样式可能包括字体、颜色、边框、填充等。使用Apache POI的基本方法复制单元格时,只会复制内容,而不会保留这些样式。
解决方案
要解决这个问题,我们需要使用Apache POI的更高级功能来复制单元格样式。这涉及到创建样式映射,以便在合并过程中跟踪和复制每个单元格的样式。
以下是实现这一功能的关键步骤:
创建样式映射:使用一个映射结构(如HashMap)来存储从原始工作簿的CellStyle到合并工作簿的XSSFCellStyle的映射。这可以确保我们不会为相同的样式重复创建新的XSSFCellStyle对象。
遍历工作簿和工作表:对于要合并的每个Excel文件,遍历其中的每个工作簿和工作表。
复制行和单元格:对于每个工作表中的行和单元格,创建新的行和单元格,并复制内容。
复制样式:在复制单元格时,检查样式映射是否已经包含了该单元格的样式。如果没有,从原始工作簿中克隆样式,并将其添加到映射中。然后,将新克隆的样式应用到新创建的单元格上。
写入合并后的工作簿:最后,将合并后的工作簿写入输出文件。
代码示例
下面是一个示例代码,展示了如何实现上述解决方案:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.0</version>
</dependency>
案例一:多个excel合成一个表格
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class ExcelMerger {
public static void main(String[] args) {
List<String> fileNames = new ArrayList<>();
fileNames.add("H:\\time\\_打卡时间表1.xlsx");
fileNames.add("H:\\time\\_打卡时间表2.xlsx");
fileNames.add("H:\\time\\_打卡时间表3.xlsx");
// 添加更多文件路径...
String outputFileName = "H:\\time\\merged_excel_file.xlsx";
try{
InputStream inputStream;Workbook workbook;
OutputStream outputStream = new FileOutputStream(outputFileName);
XSSFWorkbook mergedWorkbook = new XSSFWorkbook();
int j = 0;
for (String fileName : fileNames) {
inputStream = new FileInputStream(fileName);
workbook = WorkbookFactory.create(inputStream);
j ++;
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
Sheet sheet = workbook.getSheetAt(i);
try {
mergedWorkbook.createSheet(sheet.getSheetName() + i + j);
}catch (Exception e){
System.out.println(1);
}
copySheet(mergedWorkbook.getSheet(sheet.getSheetName() + i + j), sheet);
}
}
mergedWorkbook.write(outputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void copySheet(Sheet newSheet, Sheet sheet) {
int rowCount = sheet.getLastRowNum() - sheet.getFirstRowNum();
for (int i = 0; i <= rowCount; i++) {
Row newRow = newSheet.createRow(i);
Row currentRow = sheet.getRow(i);
copyRow(newRow, currentRow);
}
}
private static void copyRow(Row newRow, Row currentRow) {
if (currentRow == null) {
return;
}
int cellCount = currentRow.getLastCellNum();
for (int i = 0; i < cellCount; i++) {
Cell currentCell = currentRow.getCell(i);
if (currentCell == null) {
continue;
}
Cell newCell = newRow.createCell(i);
copyCell(newCell, currentCell);
}
}
private static void copyCell(Cell newCell, Cell currentCell) {
newCell.setCellType(currentCell.getCellType());
switch (currentCell.getCellType()) {
case STRING:
newCell.setCellValue(currentCell.getStringCellValue());
break;
case BOOLEAN:
newCell.setCellValue(currentCell.getBooleanCellValue());
break;
case NUMERIC:
newCell.setCellValue(currentCell.getNumericCellValue());
break;
case FORMULA:
newCell.setCellFormula(currentCell.getCellFormula());
break;
default:
break;
}
}
}
案例二:多个表格合成一个sheet
//初始化一个XSSFWorkbook实例作为合并后的工作簿,并创建一个名为"Merged Sheet"的新sheet。
//遍历每个要合并的Excel文件,并对每个文件中的每个sheet进行迭代。
//对于每个sheet中的每一行,都创建一个新的行并复制到合并后的sheet中。
//在复制行时,同时复制每个单元格的内容,包括字符串、布尔值、数字和公式。
//最后,将合并后的工作簿写入输出文件。
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class ExcelMerger {
public static void main(String[] args) {
List<String> fileNames = new ArrayList<>();
fileNames.add("H:\\time\\_打卡时间表1.xlsx");
fileNames.add("H:\\time\\_打卡时间表2.xlsx");
fileNames.add("H:\\time\\_打卡时间表3.xlsx");
// 添加更多文件路径...
String outputFileName = "H:\\time\\merged_excel_file.xlsx";
try {
InputStream inputStream; Workbook workbook;
FileOutputStream outputStream = new FileOutputStream(outputFileName);
XSSFWorkbook mergedWorkbook = new XSSFWorkbook();
Sheet mergedSheet = mergedWorkbook.createSheet("Merged Sheet");
int rowNum = 0; // 用于新sheet中的行号
for (String fileName : fileNames) {
inputStream = new FileInputStream(fileName);
workbook = WorkbookFactory.create(inputStream);
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
Sheet sheet = workbook.getSheetAt(i);
for (Row row : sheet) {
Row newRow = mergedSheet.createRow(rowNum++);
copyRow(newRow, row);
}
}
}
mergedWorkbook.write(outputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void copyRow(Row newRow, Row currentRow) {
if (currentRow == null) {
return;
}
for (Cell cell : currentRow) {
Cell newCell = newRow.createCell(cell.getColumnIndex(), cell.getCellType());
switch (cell.getCellType()) {
case STRING:
newCell.setCellValue(cell.getStringCellValue());
break;
case BOOLEAN:
newCell.setCellValue(cell.getBooleanCellValue());
break;
case NUMERIC:
newCell.setCellValue(cell.getNumericCellValue());
break;
case FORMULA:
newCell.setCellFormula(cell.getCellFormula());
break;
case BLANK:
// Handle blank cells as needed
break;
default:
break;
}
}
}
}
案例三:多个表格合成一个sheet,并保留样式
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ExcelMergerWithStyles {
public static void main(String[] args) {
List<String> fileNames = new ArrayList<>();
fileNames.add("H:\\time\\_打卡时间表1.xlsx");
fileNames.add("H:\\time\\_打卡时间表2.xlsx");
fileNames.add("H:\\time\\_打卡时间表3.xlsx");
// 添加更多文件路径...
String outputFileName = "H:\\time\\merged_excel_file.xlsx";
try {
InputStream inputStream;
Workbook workbook;
FileOutputStream outputStream = new FileOutputStream(outputFileName);
XSSFWorkbook mergedWorkbook = new XSSFWorkbook();
Sheet mergedSheet = mergedWorkbook.createSheet("Merged Sheet");
int rowNum = 0; // 用于新sheet中的行号
// 用于存储已创建样式的映射,以避免重复创建相同的样式
Map<CellStyle, XSSFCellStyle> styleMap = new HashMap<>();
for (String fileName : fileNames) {
inputStream = new FileInputStream(fileName);
workbook = WorkbookFactory.create(inputStream);
for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
Sheet sheet = workbook.getSheetAt(i);
for (Row row : sheet) {
Row newRow = mergedSheet.createRow(rowNum++);
copyRowWithStyles(newRow, row, mergedWorkbook, styleMap);
}
}
}
mergedWorkbook.write(outputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void copyRowWithStyles(Row newRow, Row currentRow, XSSFWorkbook mergedWorkbook,
Map<CellStyle, XSSFCellStyle> styleMap) {
if (currentRow == null) {
return;
}
for (Cell cell : currentRow) {
Cell newCell = newRow.createCell(cell.getColumnIndex(), cell.getCellType());
copyCellContent(cell, newCell);
// 复制样式
CellStyle cellStyle = cell.getCellStyle();
XSSFCellStyle newCellStyle = styleMap.get(cellStyle);
if (newCellStyle == null) {
newCellStyle = mergedWorkbook.createCellStyle();
newCellStyle.cloneStyleFrom(cellStyle);
styleMap.put(cellStyle, newCellStyle);
}
newCell.setCellStyle(newCellStyle);
}
}
private static void copyCellContent(Cell source, Cell target) {
switch (source.getCellType()) {
case STRING:
target.setCellValue(source.getStringCellValue());
break;
case BOOLEAN:
target.setCellValue(source.getBooleanCellValue());
break;
case NUMERIC:
target.setCellValue(source.getNumericCellValue());
break;
case FORMULA:
target.setCellFormula(source.getCellFormula());
break;
case BLANK:
// Handle blank cells as needed
break;
default:
break;
}
}
}
总结
通过扩展Apache POI库的基本功能,我们可以实现在合并Excel文件时保留原表格样式的能力。这涉及到创建样式映射,遍历工作簿和工作表,复制行、单元格和样式,并最终写入合并后的工作簿。虽然这个过程可能比简单的合并更为复杂,但它提供了更大的灵活性,允许我们根据需要定制合并后的Excel文件的样式和格式。
对于需要处理大量Excel文件或具有复杂样式的场景,这种方法可能需要进行进一步的优化和错误处理。但无论如何,它提供了一个很好的起点,用于在Java中实现具有样式保留功能的Excel合并操作。