Excel中Sheet复制

此工具通过C#实现多个Excel文件的指定工作表合并为一个文件的功能。程序首先创建一个新的工作簿,然后依次打开每个源文件并复制所有工作表到新工作簿中(除了第一个工作表)。最后保存为指定的目标文件。

        public static bool MergeSheet(string fileName, List<string> sheetViewNameList,ref string message)
        {
            if (sheetViewNameList!=null&&sheetViewNameList.Count>0)
            {
                ApplicationClass appcls = new ApplicationClass();
                Workbook wkb = null;
                object missing = System.Reflection.Missing.Value;
                appcls.DisplayAlerts = false;
                appcls.Visible = false;
                try
                {
                    //新建一工作簿 
                    wkb = appcls.Workbooks.Add(XlSheetType.xlWorksheet);
                    bool canDelete = true;
                    foreach (string file in sheetViewNameList)
                    {
                        Workbook wkbFrom = appcls.Workbooks.Open(file, missing, missing,
                                                      missing, missing, missing, missing,
                                                      missing, missing, missing, missing,
                                                      missing, missing);
                        Worksheet wst = null;

                        for (int i = 1; i <= wkbFrom.Sheets.Count; i++)
                        {
                            wst = null;
                            //取得sheet
                            wst = (Excel.Worksheet)(wkbFrom.Sheets.get_Item(i));
                            //将sheet复制到工作簿中
                            wst.Copy(missing, wkb.Worksheets[wkb.Worksheets.Count]);
                            if (canDelete)
                            {
                                //将第一个sheet即sheet1删除
                                ((Worksheet)wkb.Worksheets.get_Item(1)).Delete();
                                canDelete = false;
                            }
                        }
                        wst = null;
                        wkbFrom.Close(missing, missing, missing);
                        wkbFrom = null;
                    }
                    //选择第一个Sheet
                    ((Worksheet)wkb.Worksheets.get_Item(1)).Select(missing);
                    //保存工作簿
                    wkb.SaveAs(fileName, XlFileFormat.xlWorkbookNormal, missing, missing, missing, missing,
                                    Excel.XlSaveAsAccessMode.xlNoChange, missing, missing, missing, missing);
                    return true;
                }
                catch (Exception ex)
                {
                    message = ex.Message;
                    foreach (string filefrom in sheetViewNameList)
                    {
                        //源文件删除

                        FileInfo fileInfo = new FileInfo(filefrom);
                        if (fileInfo.Exists)
                        {
                            //只读属性先设置成普通属性再删除
                            fileInfo.Attributes = FileAttributes.Normal;
                            fileInfo.Delete();
                        }
                    }
                    return false;
                }
                finally
                {
                    if (appcls != null)
                    {
                        appcls.Quit();
                        Marshal.FinalReleaseComObject((Object)appcls);
                    }
                    appcls = null;
                    wkb = null;
                }
            }
            else
            {
                return false;
            }
        }

<think>我们之前已经讨论过使用Apache POI复制Excel Sheet的方法。现在根据用户的问题,我将总结一个更全面的实现方案,包括跨工作簿复制和同工作簿复制。 ### 核心步骤 1. **复制合并单元格**:使用`getMergedRegions()`获取所有合并区域并复制到新Sheet 2. **复制行和单元格**:遍历每一行和每个单元格,复制内容、样式和公式 3. **复制列宽**:将源Sheet的列宽设置到目标Sheet 4. **复制行高**:复制行高 ### 完整工具类(支持.xls和.xlsx) ```java import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class PoiSheetCopier { /** * 复制整个Sheet到目标工作簿(支持同工作簿和跨工作簿) * @param sourceSheetSheet * @param targetWorkbook 目标工作簿 * @param sheetName 新Sheet名称 * @return 复制后的Sheet */ public static Sheet copySheet(Sheet sourceSheet, Workbook targetWorkbook, String sheetName) { // 创建目标Sheet Sheet targetSheet = targetWorkbook.createSheet(sheetName); // 1. 复制合并单元格(必须在创建单元格之前复制) for (int i = 0; i < sourceSheet.getNumMergedRegions(); i++) { CellRangeAddress mergedRegion = sourceSheet.getMergedRegion(i); targetSheet.addMergedRegion(mergedRegion); } // 2. 设置列宽(提前设置,避免创建单元格时自动调整列宽) for (int col = 0; col < sourceSheet.getRow(0).getLastCellNum(); col++) { targetSheet.setColumnWidth(col, sourceSheet.getColumnWidth(col)); } // 3. 复制行和单元格 for (int rowNum = sourceSheet.getFirstRowNum(); rowNum <= sourceSheet.getLastRowNum(); rowNum++) { Row sourceRow = sourceSheet.getRow(rowNum); if (sourceRow == null) continue; Row newRow = targetSheet.createRow(rowNum); newRow.setHeight(sourceRow.getHeight()); // 复制每个单元格 for (int colNum = sourceRow.getFirstCellNum(); colNum < sourceSheet.getRow(0).getLastCellNum(); colNum++) { Cell sourceCell = sourceRow.getCell(colNum); if (sourceCell == null) continue; Cell newCell = newRow.createCell(colNum); copyCell(sourceCell, newCell, targetWorkbook); } } return targetSheet; } /** * 复制单元格内容和样式 * @param sourceCell 源单元格 * @param newCell 目标单元格 * @param targetWorkbook 目标工作簿(用于创建样式) */ private static void copyCell(Cell sourceCell, Cell newCell, Workbook targetWorkbook) { // 复制样式 CellStyle newStyle = targetWorkbook.createCellStyle(); newStyle.cloneStyleFrom(sourceCell.getCellStyle()); newCell.setCellStyle(newStyle); // 复制内容 switch (sourceCell.getCellType()) { case STRING: newCell.setCellValue(sourceCell.getStringCellValue()); break; case NUMERIC: newCell.setCellValue(sourceCell.getNumericCellValue()); break; case BOOLEAN: newCell.setCellValue(sourceCell.getBooleanCellValue()); break; case FORMULA: newCell.setCellFormula(sourceCell.getCellFormula()); break; case BLANK: newCell.setBlank(); break; case ERROR: newCell.setCellErrorValue(sourceCell.getErrorCellValue()); break; default: newCell.setCellValue(sourceCell.getStringCellValue()); } } /** * 示例:跨工作簿复制Sheet */ public static void main(String[] args) throws IOException { // 读取源工作簿(假设是xlsx格式) InputStream in = PoiSheetCopier.class.getResourceAsStream("source.xlsx"); Workbook sourceWorkbook = WorkbookFactory.create(in); Sheet sourceSheet = sourceWorkbook.getSheetAt(0); // 创建目标工作簿 Workbook targetWorkbook = new XSSFWorkbook(); // 复制Sheet copySheet(sourceSheet, targetWorkbook, "CopiedSheet"); // 保存目标工作簿 OutputStream out = new FileOutputStream("target.xlsx"); targetWorkbook.write(out); targetWorkbook.close(); sourceWorkbook.close(); } } ``` ### 关键点说明 1. **顺序问题**: - 先复制合并区域(`addMergedRegion`) - 再设置列宽(`setColumnWidth`) - 最后复制行和单元格 2. **样式复制**: - 每个单元格都需要创建新样式对象(避免引用共享) - 使用`cloneStyleFrom()`深度复制样式[^2] 3. **公式处理**: - 直接复制公式字符串(`setCellFormula`) - Excel打开时会重新计算 4. **格式兼容**: - 使用`WorkbookFactory`自动识别xls/xlsx格式 - 目标工作簿类型需与源类型一致(或手动指定) ### 常见问题解决方案 1. **内存溢出**: - 使用SXSSFWorkbook处理大文件 - 分块复制数据 2. **格式错乱**: - 确保合并区域复制顺序正确 - 检查列宽复制是否完整 3. **样式过多**: - POI限制最多64000种样式 - 复用相同样式对象[^1] > 注意:此代码支持跨工作簿复制,同工作簿复制只需使用同一个`Workbook`对象即可。 §§相关问题§§ 1. 如何用POI复制Excel中的图表到新Sheet? 2. POI复制单元格时如何保持条件格式规则? 3. 如何优化大文件Sheet复制的内存占用? 4. POI处理.xls和.xlsx格式有哪些区别需要注意? 5. 复制Sheet时如何跳过隐藏行或列? </think> # Java实现Excel Sheet复制功能详解 在Java中,使用Apache POI库可以高效实现Excel Sheet复制功能。下面我将详细介绍完整的实现方案,并提供优化建议。 ## 核心实现代码 ```java import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.FileInputStream; import java.io.FileOutputStream; public class ExcelSheetCopier { public static void main(String[] args) throws Exception { // 1. 加载源工作簿 Workbook sourceWorkbook = WorkbookFactory.create(new FileInputStream("source.xlsx")); Sheet sourceSheet = sourceWorkbook.getSheet("Sheet1"); // 2. 创建目标工作簿 Workbook targetWorkbook = new XSSFWorkbook(); // 3. 复制Sheet Sheet targetSheet = copySheet(sourceSheet, targetWorkbook, "CopiedSheet"); // 4. 保存目标工作簿 try (FileOutputStream out = new FileOutputStream("target.xlsx")) { targetWorkbook.write(out); } } public static Sheet copySheet(Sheet sourceSheet, Workbook targetWorkbook, String newSheetName) { // 创建目标Sheet Sheet targetSheet = targetWorkbook.createSheet(newSheetName); // 复制合并单元格区域 for (int i = 0; i < sourceSheet.getNumMergedRegions(); i++) { CellRangeAddress mergedRegion = sourceSheet.getMergedRegion(i); targetSheet.addMergedRegion(mergedRegion); } // 复制行和单元格 for (int rowNum = sourceSheet.getFirstRowNum(); rowNum <= sourceSheet.getLastRowNum(); rowNum++) { Row sourceRow = sourceSheet.getRow(rowNum); if (sourceRow == null) continue; Row newRow = targetSheet.createRow(rowNum); newRow.setHeight(sourceRow.getHeight()); // 复制单元格 for (int colNum = sourceRow.getFirstCellNum(); colNum < sourceRow.getLastCellNum(); colNum++) { Cell sourceCell = sourceRow.getCell(colNum); if (sourceCell == null) continue; Cell newCell = newRow.createCell(colNum); copyCell(sourceCell, newCell, targetWorkbook); } } // 复制列宽 for (int i = 0; i < sourceSheet.getRow(0).getLastCellNum(); i++) { targetSheet.setColumnWidth(i, sourceSheet.getColumnWidth(i)); } return targetSheet; } private static void copyCell(Cell sourceCell, Cell newCell, Workbook targetWorkbook) { // 复制样式 CellStyle newCellStyle = targetWorkbook.createCellStyle(); newCellStyle.cloneStyleFrom(sourceCell.getCellStyle()); newCell.setCellStyle(newCellStyle); // 复制内容 switch (sourceCell.getCellType()) { case STRING: newCell.setCellValue(sourceCell.getStringCellValue()); break; case NUMERIC: newCell.setCellValue(sourceCell.getNumericCellValue()); break; case BOOLEAN: newCell.setCellValue(sourceCell.getBooleanCellValue()); break; case FORMULA: newCell.setCellFormula(sourceCell.getCellFormula()); break; case BLANK: newCell.setBlank(); break; default: newCell.setCellValue(sourceCell.getStringCellValue()); } } } ``` ## 关键功能说明 1. **样式复制**: - 使用`cloneStyleFrom()`方法深度复制单元格样式(包括字体、边框、背景色等) - 为每个单元格创建新的样式对象,避免样式引用共享问题[^2] 2. **合并单元格处理**: - 使用`getMergedRegions()`获取所有合并区域 - 通过`addMergedRegion()`复制合并区域到目标Sheet - 必须在创建单元格**前**复制合并区域 3. **数据类型支持**: - 文本/数字/布尔值:直接复制值 - 公式:保留原始公式字符串 - 空白单元格:设置为空白 4. **格式兼容性**: - `.xlsx`格式使用`XSSFWorkbook` - `.xls`格式使用`HSSFWorkbook`(需少量调整) ## 使用示例 ### 同工作簿内复制 ```java Workbook workbook = new XSSFWorkbook(); Sheet originalSheet = workbook.createSheet("Original"); // ...填充数据... // 同工作簿复制 Sheet copiedSheet = ExcelSheetCopier.copySheet( originalSheet, workbook, "CopiedSheet" ); ``` ### 跨工作簿复制 ```java // 加载源工作簿 Workbook sourceWorkbook = WorkbookFactory.create(new FileInputStream("source.xlsx")); Sheet sourceSheet = sourceWorkbook.getSheet("Data"); // 创建目标工作簿 Workbook targetWorkbook = new XSSFWorkbook(); // 跨工作簿复制 ExcelSheetCopier.copySheet( sourceSheet, targetWorkbook, "CopiedData" ); ``` ## 性能优化建议 1. **大文件处理**: ```java // 使用SXSSFWorkbook处理大文件 Workbook targetWorkbook = new SXSSFWorkbook(100); // 100行在内存中 ``` 2. **样式复用**: ```java // 创建样式缓存 Map<CellStyle, CellStyle> styleCache = new HashMap<>(); private CellStyle getCachedStyle(CellStyle sourceStyle, Workbook targetWorkbook) { if (!styleCache.containsKey(sourceStyle)) { CellStyle newStyle = targetWorkbook.createCellStyle(); newStyle.cloneStyleFrom(sourceStyle); styleCache.put(sourceStyle, newStyle); } return styleCache.get(sourceStyle); } ``` 3. **分批处理**: 对于超大文件,可分批次复制行数据,避免内存溢出。 ## 常见问题解决 1. **样式丢失问题**: - 确保每个单元格都有独立的样式对象 - 不要直接使用`sourceCell.getCellStyle()`赋值 2. **公式错误**: - 保留原始公式字符串 - Excel打开时会自动重新计算 - 如需立即计算,可添加: ```java Workbook workbook = ...; workbook.getCreationHelper().createFormulaEvaluator().evaluateAll(); ``` 3. **列宽异常**: - 单独处理列宽复制: ```java for (int i = 0; i < maxColumn; i++) { targetSheet.setColumnWidth(i, sourceSheet.getColumnWidth(i)); } ``` 4. **隐藏行列处理**: ```java // 复制行时检查是否隐藏 if (sourceRow.getZeroHeight()) { newRow.setZeroHeight(true); } // 复制列隐藏状态 targetSheet.setColumnHidden(colIndex, sourceSheet.isColumnHidden(colIndex)); ``` ## 总结 使用Apache POI复制Excel Sheet需要处理以下核心问题: - 单元格内容和样式的深度复制 - 合并区域的正确转移 - 行高和列宽的精确复制 - 公式和特殊格式的保留 上述方案提供了完整的实现,适用于大多数业务场景。对于特殊需求(如图表、条件格式等),需要额外处理相关对象[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值