poi多Excel文件拼接合并为单文件

  作者:记性不好的阁主

需求:导出全院排班明细表

现状:目前只能导出单科室排班明细


目标:全院所有科室拼接在同一个sheet中


Excel多文件合并工具类(将多个Excel合并为单Excel文件的多个Sheet)

package org.example;

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.util.CellRangeAddress;
 
 
import java.io.*;
import java.util.Iterator;
import java.util.List;
 
public class ExcelFileMergeUtils {
 
    /**
     * #合并多个excel文件
     * @param fileLists excel文件路径
     * @param path 目标文件保存目录
     * @param fileName 目标文件名称
     */
    public static void mergeExcel(List<String> fileLists, String path, String fileName) {
        // 创建新的excel工作簿
        HSSFWorkbook newExcelWorkBook = new HSSFWorkbook();
        // 遍历需要合并的excel文件
        for (String excelName : fileLists) {
            try (InputStream in = new FileInputStream(excelName)) {
                // 创建工作簿
                HSSFWorkbook tmpWorkBook = new HSSFWorkbook(in);
                // 获取工作簿中的Sheet个数
                int len = tmpWorkBook.getNumberOfSheets();
                if (len <= 1) {
                    HSSFSheet tmpSheet = tmpWorkBook.getSheetAt(0);
                    HSSFSheet newExcelSheet = newExcelWorkBook.createSheet(tmpSheet.getSheetName());
                    // 复制sheet内容
                    copyExcelSheet(newExcelWorkBook, tmpSheet, newExcelSheet);
                } else {
                    for (int i = 0; i < len; i++) {
                        HSSFSheet tmpSheet = tmpWorkBook.getSheetAt(i);
                        HSSFSheet newExcelSheet = newExcelWorkBook.createSheet(tmpSheet.getSheetName());
                        // 复制sheet内容
                        copyExcelSheet(newExcelWorkBook, tmpSheet, newExcelSheet);
                    }
                }
                // 关闭tmpWorkBook工作簿
                tmpWorkBook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 新生成的excel文件
        if (!fileName.endsWith(".xlsx") && !fileName.endsWith(".xls")) {
            fileName += ".xlsx";
        }
        String excelFileName = path + File.separator + fileName;
        // 判断文件是否存在
        File excelFile = new File(excelFileName);
        if (excelFile.exists()) {
            // 存在则删除
            excelFile.delete();
        }
        // 使用输出流写出
        try (FileOutputStream fos = new FileOutputStream(excelFileName)) {
            newExcelWorkBook.write(fos);
            fos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                newExcelWorkBook.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("excel文件合并成功,合并后文件路径:" + excelFileName);
    }
 
    /**
     * #复制sheet到新的excel文件中
     * @param workbook excel工作簿
     * @param tmpSheet 来源sheet
     * @param newExcelSheet 新生成的sheet
     */
    public static void copyExcelSheet(HSSFWorkbook workbook, HSSFSheet tmpSheet, HSSFSheet newExcelSheet) {
        // 合并单元格
        mergeSheetAllRegion(tmpSheet, newExcelSheet);
        // 设置单元格列宽度
        // 获取最后一个单元格位置
        int len = tmpSheet.getRow(tmpSheet.getFirstRowNum()).getLastCellNum();
        for (int i = 0; i < len; i++) {
            newExcelSheet.setColumnWidth(i, tmpSheet.getColumnWidth(i));
        }
        // 复制每行内容
        Iterator<Row> it = tmpSheet.iterator();
        while (it.hasNext()) {
            HSSFRow tmpRow = (HSSFRow) it.next();
            // 创建新行
            HSSFRow newExcelRow = newExcelSheet.createRow(tmpRow.getRowNum());
            // 复制行
            copyExcelRow(workbook, tmpRow, newExcelRow);
        }
    }
 
    /**
     * #合并单元格
     * @param tmpSheet 来源sheet
     * @param newExcelSheet 目标sheet
     */
    private static void mergeSheetAllRegion(HSSFSheet tmpSheet, HSSFSheet newExcelSheet) {
        int num = tmpSheet.getNumMergedRegions();
        CellRangeAddress cellRange = null;
        for (int i = 0; i < num; i++) {
            cellRange = tmpSheet.getMergedRegion(i);
            newExcelSheet.addMergedRegion(cellRange);
        }
    }
 
    /**
     * #复制excel中的行到新的sheet中
     * @param workbook 目标工作簿
     * @param tmpRow 来源excel行
     * @param newExcelRow 目标excel行
     */
    public static void copyExcelRow(HSSFWorkbook workbook, HSSFRow tmpRow, HSSFRow newExcelRow) {
        // 设置行高
        newExcelRow.setHeight(tmpRow.getHeight());
        // 获取所有列
        Iterator<Cell> it = tmpRow.cellIterator();
        while (it.hasNext()) {
            HSSFCell tmpCell = (HSSFCell) it.next();
            // 创建单元格
            HSSFCell newExcelCell = newExcelRow.createCell(tmpCell.getColumnIndex());
            // 复制单元格
            copyExcelCell(workbook, tmpCell, newExcelCell);
        }
    }
 
    /**
     * #复制单元格
     * @param workbook 目标工作簿
     * @param tmpCell 来源excel单元格
     * @param newExcelCell 目标excel单元格
     */
    public static void copyExcelCell(HSSFWorkbook workbook, HSSFCell tmpCell, HSSFCell newExcelCell) {
        HSSFCellStyle newExcelStyle = workbook.createCellStyle();
        // 复制单元格样式
        newExcelStyle.cloneStyleFrom(tmpCell.getCellStyle());
        // 单元格样式
        newExcelCell.setCellStyle(newExcelStyle);
        if (tmpCell.getCellComment() != null) {
            newExcelCell.setCellComment(tmpCell.getCellComment());
        }
        // 不同数据类型处理
        CellType tmpCellType = tmpCell.getCellType();
 
        newExcelCell.setCellType(tmpCellType);
        if (tmpCellType == CellType.NUMERIC) {
            if (DateUtil.isCellDateFormatted(tmpCell)) {
                newExcelCell.setCellValue(tmpCell.getDateCellValue());
            } else {
                newExcelCell.setCellValue(tmpCell.getNumericCellValue());
            }
        } else if (tmpCellType == CellType.STRING) {
            newExcelCell.setCellValue(tmpCell.getRichStringCellValue());
        } else if (tmpCellType == CellType.BLANK) {
        } else if (tmpCellType == CellType.BOOLEAN) {
            newExcelCell.setCellValue(tmpCell.getBooleanCellValue());
        } else if (tmpCellType == CellType.ERROR) {
            newExcelCell.setCellErrorValue(tmpCell.getErrorCellValue());
        } else if (tmpCellType == CellType.FORMULA) {
            newExcelCell.setCellFormula(tmpCell.getCellFormula());
        } else {
        }
    }
 
}

多sheet合并器(注:单文件多个sheet名称不能重复)

package com.bozhong.nursebase.common.utils.excel;

import com.bozhong.nurse.common.utils.MinioClientUtil;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;

import java.io.*;
import java.util.Iterator;

/**
 * 多sheet合并器
 * @author 徐顺洁
 * @date 2024年03月18日 21:04:42
 **/
public class ExcelSheetMerger {

    // 工作簿游标
    private int destNum = 0;
    // 工作簿首行游标
    private int sheetFirstRowCursor = 0;
    // 是否第一个sheet标志
    private boolean firstSheel = true;
    // 是否已经复制过列宽(列宽只复制一次就行)
    private boolean copyedColumnWith = false;

    // 单文件多sheet的Excel合并到同一个sheet的最终Excel文件
    public static byte[] getMergerExcel(String multiSheetExcelName, String targetSheetName) {
        try {
            ExcelSheetMerger sheetMerger = new ExcelSheetMerger();
            InputStream is = MinioClientUtil.download(MinioClientUtil.getTemporaryBucket(), multiSheetExcelName);
            HSSFWorkbook workbook;
            workbook = new HSSFWorkbook(is);
            is.close();
            HSSFWorkbook targetWorkbook = new HSSFWorkbook();
            targetWorkbook.createSheet(targetSheetName); // 目标sheet
            Sheet targetSheet = targetWorkbook.getSheetAt(0);
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                Sheet sourceSheet = workbook.getSheetAt(i);
                sheetMerger.copySheet(sourceSheet, targetSheet, targetWorkbook);
                sheetMerger.copyMerge(sourceSheet, targetSheet, targetWorkbook);
            }
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            targetWorkbook.write(out);
            out.close();
            targetWorkbook.close();
            return out.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void copyMerge(Sheet source, Sheet target, Workbook workbook) {
        // 复制合并单元格信息
        for (int i = 0; i < source.getNumMergedRegions(); i++) {
            CellRangeAddress cellRangeAddress = source.getMergedRegion(i);
            int firstRow = cellRangeAddress.getFirstRow();
            int lastRow = cellRangeAddress.getLastRow();
            int firstCol = cellRangeAddress.getFirstColumn();
            int lastCol = cellRangeAddress.getLastColumn();
            target.addMergedRegion(new CellRangeAddress(firstRow+sheetFirstRowCursor, lastRow+sheetFirstRowCursor, firstCol, lastCol));
        }
    }

    private void copySheet(Sheet source, Sheet target, Workbook workbook) {
        int rowNum = 0;
        while (source.getRow(rowNum) != null) {
            Row sourceRow = source.getRow(rowNum);
            Row destRow = target.createRow(destNum++);
            copyRow(sourceRow, destRow, workbook);
            rowNum++;
        }
        if (!copyedColumnWith) {
            // 列宽复制
            for (Iterator rowIt = source.rowIterator(); rowIt.hasNext();) {
                HSSFRow sourceRow = (HSSFRow) rowIt.next();
                // 设置列宽
                for (int i = 0; i < sourceRow.getLastCellNum(); i++) {
                    target.setColumnWidth(i, source.getColumnWidth(i));
                }
            }
            copyedColumnWith = true;
        }
        if (!firstSheel) {
            sheetFirstRowCursor = destNum - rowNum;
        } else {
            firstSheel = false;
        }
    }

    private void copyRow(Row source, Row target, Workbook workbook) {
        target.setHeight(source.getHeight());
        for (int i = 0; i < source.getLastCellNum(); i++) {
            Cell sourceCell = source.getCell(i);
            Cell destCell = target.createCell(i);
            if (sourceCell != null) {
                copyCell(sourceCell, destCell, workbook);
            }
        }
    }

    private void copyCell(Cell source, Cell target, Workbook workbook) {
        CellStyle style = workbook.createCellStyle();
        style.cloneStyleFrom(source.getCellStyle());

        target.setCellStyle(style);

        switch (source.getCellType()) {
            case STRING:
                target.setCellValue(source.getStringCellValue());
                break;
            case NUMERIC:
                target.setCellValue(source.getNumericCellValue());
                break;
            case BOOLEAN:
                target.setCellValue(source.getBooleanCellValue());
                break;
            case FORMULA:
                target.setCellFormula(source.getCellFormula());
                break;
            case BLANK:
                // target.setCellType(CellType.BLANK);
                break;
            case ERROR:
                target.setCellErrorValue(source.getErrorCellValue());
                break;
        }
    }

}

使用方式:

1、将多个Excel合并为单Excel文件的多个Sheet


    public static void main(String[] args) {
        String dir = "src\\main\\java\\org\\example\\";
        //这里是xls文件
        String[] filePaths = {"2022.08.22~2022.08.28(第35周)全院排班表-1.xls",
                "2022.08.22~2022.08.28(第35周)全院排班表-2.xls", "2022.08.22~2022.08.28(第35周)全院排班表-3.xls", "2022.08.22~2022.08.28(第35周)全院排班表-4.xls"};
 
        ArrayList<String> list = new ArrayList<>();
 
        for (String path : filePaths) {
            list.add(dir+path);
        }
 
        String path = "文件合并后存放路径";
        String fileName = "test.xls";
        ExcelFileMergeUtils.mergeExcel(list, dir, fileName);
    }

2、多sheet合并器(注:单文件多个sheet名称不能重复)


    public static void main(String[] args) throws IOException {
        ExcelSheetMerger sheetMerger = new ExcelSheetMerger();
        String dir = "src\\main\\java\\org\\example\\";
        InputStream is = new FileInputStream(dir+"test.xls"); // 源xls文件
        HSSFWorkbook workbook = new HSSFWorkbook(is);
        is.close();
        HSSFWorkbook targetWorkbook = new HSSFWorkbook();
        targetWorkbook.createSheet("全院"); // 目标sheet
        Sheet targetSheet = targetWorkbook.getSheetAt(0);
        for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
            Sheet sourceSheet = workbook.getSheetAt(i);
            sheetMerger.copySheet(sourceSheet, targetSheet, targetWorkbook);
            sheetMerger.copyMerge(sourceSheet, targetSheet, targetWorkbook);
        }
 
        FileOutputStream fileOut = new FileOutputStream(dir+"merged.xls");
        targetWorkbook.write(fileOut);
        fileOut.close();
    }

* 使用线程池优化导出效率



    // 全院多线程导出设置锁保证线程安全
    private final ReentrantLock scheduleExportLock = new ReentrantLock();
	public void exportMergeExcel() {
	    // 多个单科室导出Excel文件列表,线程安全集合
	    List<String> deptFileNames = Collections.synchronizedList(new ArrayList<>());

	    CountDownLatch processCountDownLatch = new CountDownLatch(nurseUnitInfoList.size());
	    // 定义线程数为科室总数,每当调用processCountDownLatch.countDown();一次就会减去1,减到0后执行主线程
	    for (NurseUnitInfoRespDTO nurseUnitInfo : nurseUnitInfoList) {
	        ThreadUtil.runForProcessScheduleExport(() -> {
	            // 【非公共资源变量等在这个代码块里】
		        try {
		            // 设置锁超时时间2分钟,超时后自动释放锁防止死锁
		            if (scheduleExportLock.tryLock(120, TimeUnit.SECONDS)) {
		                try {
		                	【公共资源变量等在这个代码块里】
		                    // 导出逻辑省略...
		                } catch (IOException e) {
		                    throw new RuntimeException(e);
		                } finally {
		                    scheduleExportLock.unlock();
		                }
		            }
		        } catch (Exception e) {
		            Thread.currentThread().interrupt();
		            log.error("scheduleExportLock ---> Interrupted while trying to acquire lock. 导出发生错误:", e);
		        } finally {
                    processCountDownLatch.countDown();
                }
		        deptFileNames.add(deptFileName);
	        }
	    }
	    try {
		    processCountDownLatch.await();
		    log.info(">>>>>>>>>>>>>>>>>>>>>主线程等待所有科室排班明细导出结束<<<<<<<<<<<<<<<<<<<<<<<");
	    } catch (InterruptedException e) {
	        e.printStackTrace();
	    }
	    // 多文件首sheet合并为单文件多sheet
	    byte[] mergeBuffer = ExcelFileMergeUtils.mergeExcel(deptFileNames);
	    // 单文件多sheet合并为单文件首sheet并且命名为“全院排班明细”
	    byte[] mergedBuffer = ExcelSheetMerger.getMergerExcel(fileName, "全院排班明细");
	}


相关推荐

评论 抢沙发

表情

分类选择