Java实现把压缩里的Excel文件合并为一个Excel

  • 一、情景描述
  • 二、实现方案
  • 三、实现思路
  • 四、打包部署


一、情景描述

最近在项目中客户提到一个需求,客户从业务系统中下载的数据表是一个压缩包形式(如下图:),客户希望做一个小工具把从系统上下载的压缩包里面的所有数据表合并为一个Excel 文件;

在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_java


压缩包里面每个文件夹是以订单号命名的,每个文件夹里面是每个订单的信息表;如下图:

在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_压缩包_02


在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_数据_03


信息表示例:

在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_上传_04


合并后的期望效果,如下图:

在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_excel_05

二、实现方案

第一种方案:
考虑到如果做成网页版的解析工具,开发好的软件需要部署到服务器上,然后通过域名或者IP+端口号才能访问该网站;因为这样不太方便用户的使用,这种方案不太满足,所以没有采用该方案;
第二种方案:
Java GUI—Java图形用户界面,这种方式用户可以通过鼠标、键盘或者触摸等输入设备与计算机进行交互,使用户能够更轻松、更高效地完成各种任务;故采用此方案;

三、实现思路

1、首先用GUI中Swing组件搭建出一个图形化界面;

public FileUploadApp() {
        setTitle("数据解析工具客户端v1");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 600, 400);
        contentPane = new JPanel();
        setContentPane(contentPane);
        contentPane.setLayout(new BorderLayout(0, 0));

        JPanel panel = new JPanel();
        FlowLayout flowLayout = (FlowLayout) panel.getLayout();
        flowLayout.setAlignment(FlowLayout.LEFT);
        contentPane.add(panel, BorderLayout.NORTH);

        btnZipUpload = new JButton("导入压缩包");
        btnZipUpload.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                uploadZipFile(contentPane);
            }
        });
        panel.add(btnZipUpload);

        btnTemplateUpload = new JButton("导入模板");
        btnTemplateUpload.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                uploadTemplateFile(contentPane);
            }
        });
        panel.add(btnTemplateUpload);

        parsingExcel = new JButton("解析Excel");
        parsingExcel.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                String message = parsingExcelFile();
                JOptionPane.showMessageDialog(null, message);
            }
        });
        panel.add(parsingExcel);

        textArea = new JTextArea();
        textArea.setEditable(false);
        contentPane.add(textArea, BorderLayout.SOUTH);
}

效果如下:

在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_excel_06


2、用户分别上传压缩包、模板,然后点解析Excel按钮,系统开始解析;

(1) 如果用户还没有上传压缩包或者模板,直接点了解析Excel,系统会进行判断;

String zipPath = zipFile.getAbsolutePath();
String templatePath = templateFile.getAbsolutePath();
if(StringUtil.isBlank(zipPath) ){
   return "请上传需要解析的压缩包";
}else if (StringUtil.isBlank(templatePath)){
   return "请上传模板Excel文件";
}

(2) 压缩包和模板都上传后,系统开始解析
① 首先通过ZipFile工具类,打开压缩包文件;

// 打开压缩包文件
ZipFile zipFile = new ZipFile(zipFilePath);
Enumeration<? extends ZipEntry> entries = zipFile.entries();

② 然后通过导入的模板路径,创建Workbook对象,并且获得sheet和设置日期格式;

Workbook outputWorkbook = new XSSFWorkbook(new FileInputStream(outputFilePath));
Sheet outputSheet = outputWorkbook.getSheet("Sheet1");
//设置日期格式
CellStyle dateCellStyle = outputWorkbook.createCellStyle();
dateCellStyle.setDataFormat(outputWorkbook.getCreationHelper().createDataFormat().getFormat("yyyy-MM-dd"));

③ 定义当前行的下标变量,初始值为1,目的为了实现内容叠加的效果;

int rowNum = 1;//定义当前行的下标

④ 开始while循环遍历,每次判断Enumeration对象中是否还有数据,有数据继续执行,没有数据终止循环;
⑤ 然后通过方法获取Enumeration 对象中的下一个数据,通过方法判断是否是Excel文件,如果是开始读取Excel文件内容,遍历每一行并写入新的Excel文件;

ZipEntry entry = entries.nextElement();
// 确认是Excel文件
if (!entry.isDirectory() && entry.getName().endsWith(".xlsx"))
    InputStream inputStream = zipFile.getInputStream(entry);
    // 读取Excel文件内容
    Workbook inputWorkbook = new XSSFWorkbook(inputStream);
    Sheet inputSheet = inputWorkbook.getSheetAt(0);
    //遍历每一行并写入新的 Excel 文件
    //使用 Iterator<Row> 来遍历输入工作表的每一行,并对每一行进行处理
    Iterator<Row> rowIterator = inputSheet.iterator();

⑥ 获取当前Excel的当前行号,判断当前行的下标是否大于0,大于0往下执行,因为下标0是每个转关信息表的表头,这样每次会忽略表头不进行合并;

Row inputRow = rowIterator.next();//当前行
int inputRownum = inputRow.getRowNum();//获取当前行号
if(inputRownum > 0)

⑦ 通过自定义的ExcelUtils工具类判断Excel文件中每一行有没有数据,没有数据直接跳过该文件;

//如果这行没有数据,直接跳过
if(ExcelUtils.isEmptyRow(inputRow)){
    break;
}

ExcelUtils.isEmptyRow()方法具体代码如下:

public static boolean isEmptyRow(Row row){
   //行不存在
   if (row == null) {
       return true;
   }
   //第一个列位置
   int firstCellNum = row.getFirstCellNum();
   //最后一列位置
   int lastCellNum = row.getLastCellNum();
   //空列数量
   int nullCellNum = 0;
   for (int c = firstCellNum; c < lastCellNum; c++) {
        Cell cell = row.getCell(c);
        if (null == cell || CellType.BLANK == cell.getCellType()) {
            nullCellNum ++;
            continue;
        }
        cell.setCellType(CellType.STRING);
        String cellValue = cell.getStringCellValue().trim();
        if (StringUtil.isBlank(cellValue)) {
           nullCellNum ++;
        }
   }
   //所有列都为空
   if (nullCellNum == (lastCellNum - firstCellNum)) {
       return true;
   }
   return false;
}

⑧ 如果这一行有数据,开始遍历每一列,然后对单元格的格式进行判断,并添加到模板的Cell中;

// 遍历每一列
Iterator<Cell> cellIterator = inputRow.cellIterator();
while (cellIterator.hasNext()) {//对单元格的格式进行判断
      Cell inputCell = cellIterator.next();
      Cell outputCell = outputRow.createCell(inputCell.getColumnIndex());
      switch (inputCell.getCellType()){
             case STRING://字符串类型
                 outputCell.setCellValue(inputCell.getStringCellValue());
                 break;
             case NUMERIC://数值类型
                 if (DateUtil.isCellDateFormatted(inputCell)) {
                     // 设置日期单元格的值和格式
                     outputCell.setCellValue(inputCell.getDateCellValue());
                     outputCell.setCellStyle(dateCellStyle);
                 } else {
                     outputCell.setCellValue(inputCell.getNumericCellValue());
                 }
                 break;
             case BOOLEAN://布尔类型
                  outputCell.setCellValue(inputCell.getBooleanCellValue());
                  break;
             case FORMULA://公式类型
                  outputCell.setCellFormula(inputCell.getCellFormula());
                  break;
             case BLANK://空单元格
                  break;
             default:
                 break;
     }
}

⑨ 最后通过FileOutputStream对象获取模板的输出流,并且把模板的Workbook写入到输出流中;

FileOutputStream outputFileStream = new FileOutputStream(outputFilePath);
outputWorkbook.write(outputFileStream);

⑩ 合并完毕

四、打包部署

1、项目的开发使用的是Maven搭建的,首先在pom文件进行配置;

<build>
     <finalName>excelTools-1.0-SNAPSHOT</finalName><!-- 导出jar的名字 -->
     <plugins>
         <plugin>
             <groupId>org.apache.maven.plugins</groupId>
             <artifactId>maven-shade-plugin</artifactId>
             <version>3.2.0</version>
             <executions>
             	<execution>
             		<phase>package</phase>
                    	<goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>com.trans.eurasia.gui.FileUploadApp</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
         </plugin>
     </plugins>
  </build>

2、Java的开发工具使用的Idea,我们直接使用右侧Maven中的Lifecycle中的package指令上双击;

在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_数据_07


3、此时经过Maven的编译打包,即可在我们项目路径的target目录下生成项目的jar包;

在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_压缩包_08


在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_上传_09


4、我们可通过cmd窗口执行我们的jar包,命令为:java -jar XX.jar

在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_excel_10


5、如果使用cmd的方式去执行项目,首先要满足每个电脑有Java的运行环境才可以通过命令的方法执行开发的软件,考虑到这点,我使用exe4j工具将jar包打包为exe格式的可执行程序;

在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_java_11


6、通过exe4j工具打包后的效果如下:

在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_java_12


我们直接双击“数据解析工具.exe”,即可打开我们的工具:

在Java中使用EasyExcel合并固定多列表头 java将多个excel合并_上传_13