夜雨聆风学习资料网

ARTICLE · 1201520

Java 使用 POI 操作 Excel

Java 使用 POI 操作 Excel

实际工作中经常会遇到这样的场景:某些业务功能需要将一些数据生成 Excel 文件并提供给用户,也会让用户根据 Excel 模板录入一些数据,程序读取 Excel 中的数据进行处理,最终存储到数据库中。对于 Java 来说,POI 是最常用来处理 Excel 文件内容的组件。POI 组件功能很强大,不但可以处理 Excel ,也可以处理 Word、PPT 等 Office 文件内容。

本篇博客只简单介绍有关 POI 处理 Excel 的常用方法,在博客的最后面会提供 demo 的源代码。

一、导入 jar 包

搭建一个 maven 工程,导入 poi-ooxml 的 jar 包,具体内容如下:

有关具体的 jar 包地址,可以在 https://mvnrepository.com 上进行查询。

<!--poi-ooxml 包提供对后缀名 .xlsx 的 Excel 文件的支持--><dependency>    <groupId>org.apache.poi</groupId>    <artifactId>poi-ooxml</artifactId>    <version>5.2.1</version></dependency><!--导入 junit 的 jar 包--><dependency>    <groupId>junit</groupId>    <artifactId>junit</artifactId>    <version>4.12</version>    <scope>test</scope></dependency>

目前的 Excel 文件绝大多数情况下都是采用 Excel2007 以及之后的高版本 Excel 创建的(文件名以 .xlsx 结尾),因此只需要导入 poi-ooxml 这一个 jar 包即可。另外也把 juint 的 jar 包导入,方便进行独立测试方法的编写。最后打开右侧的 Maven 窗口,刷新一下,这样 Maven 会自动下载所需的 jar 包文件。

搭建好的项目工程整体目录比较简单,具体如下图所示:

项目工程结构简单介绍:

com.jobs.employee 主要就是一个 Java Bean 实体类

在 test 目录下 com.jobs.exceltest 用来编写操作 Excel 的测试方法

在 src 目录下的 tempfile 目录,用来存放生成的 excel 文件

二、细节展示

com.jobs.employee 是一个 Java Bean 的实体类,主要用来承载数据,具体内容如下:

package com.jobs;import java.util.Date;public class employee {    //姓名    private String empName;    //每日薪水    private Integer dayMoney;    //出勤天数    private Integer workDays;    //领取薪水的时间    private Date payTime;    public employee() {    }    public employee(String empName, Integer dayMoney, Integer workDays, Date payTime) {        this.empName = empName;        this.dayMoney = dayMoney;        this.workDays = workDays;        this.payTime = payTime;    }    //相关字段的 Get 和 Set 方法,省略...}

com.jobs.exceltest 中有两个方法,分别用来测试生成 Excel 和读取 Excel,具体内容如下:

package com.jobs;import org.apache.poi.ss.usermodel.*;import org.apache.poi.xssf.usermodel.*;import org.junit.Test;import java.io.*;import java.util.*;import java.text.SimpleDateFormat;public class exceltest {    @Test    public void WriteExcel() throws Exception {        //创建一个文件对象,作为excel文件内容的输出文件        //本 demo 将 excel 文件生成到工程项目下的 tempfile 目录中        File f = new File("tempfile/money.xlsx");        try (Workbook wb = new XSSFWorkbook();             FileOutputStream os = new FileOutputStream(f)) {            //.xls 的 excel 文件使用 HSSFWorkbook(现在基本上很少用到了)            //.xlsx 的 excel 文件使用 XSSFWorkbook            Sheet st = wb.createSheet("员工薪水");            //在这里统一设置单元格样式,后续给相关单元格设置该样式            //对于 excel 第一行的标题,设置样式为字体加粗,居中显示            CellStyle cstitle = wb.createCellStyle();            cstitle.setAlignment(HorizontalAlignment.CENTER);            Font font = wb.createFont();            font.setBold(true);            cstitle.setFont(font);            //对于 excel 从第二行开始的数据行,设置            CellStyle csfield = wb.createCellStyle();            csfield.setAlignment(HorizontalAlignment.CENTER);            //对于日期型的数据,设置其显示样式            CellStyle csdate = wb.createCellStyle();            csdate.setAlignment(HorizontalAlignment.CENTER);            csdate.setDataFormat(wb.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));            //将第 5 列(日期所在的列)的列宽设置的大一些            //参数为:列的索引,列宽 * 256 的计算值(这个是设置列宽的固定写法)            st.setColumnWidth(5, 5000);             //从数据库获取数据,这里造一些假数据            List<employee> emplist = new ArrayList<>();            Calendar calendar = Calendar.getInstance();            //Calendar 的月份是从 0 开始的,因此下面的时间是 2022-04-05 10:11:20            calendar.set(2022, 3, 5, 10, 11, 20);            emplist.add(new employee("候胖胖", 200, 21, calendar.getTime()));            calendar.add(Calendar.MINUTE, 12);            emplist.add(new employee("任肥肥", 180, 22, calendar.getTime()));            calendar.add(Calendar.MINUTE, 15);            emplist.add(new employee("李敦敦", 150, 25, calendar.getTime()));            //第一行标题            String[] titles = {"序号", "姓名", "每日薪水", "出勤天数", "薪水统计", "领取时间"};            Row rowtitle = st.createRow(0);            for (int i = 0; i < titles.length; i++) {                Cell celltitle = rowtitle.createCell(i);                celltitle.setCellValue(titles[i]);                celltitle.setCellStyle(cstitle);            }            //从第二行开始写数据            int rowindex = 1;            for (employee emp : emplist) {                Row rowtemp = st.createRow(rowindex);                //序号                Cell Cell0 = rowtemp.createCell(0);                Cell0.setCellValue(rowindex);                Cell0.setCellStyle(csfield);                //姓名                Cell Cell1 = rowtemp.createCell(1);                Cell1.setCellValue(emp.getEmpName());                Cell1.setCellStyle(csfield);                //每日薪水                Cell Cell2 = rowtemp.createCell(2);                Cell2.setCellValue(emp.getDayMoney());                Cell2.setCellStyle(csfield);                //出勤天数                Cell Cell3 = rowtemp.createCell(3);                Cell3.setCellValue(emp.getWorkDays());                Cell3.setCellStyle(csfield);                //薪水统计(使用 excel 的计算公式)                //注意:excel的行号和列号是从 1 开始的                int formulaRowIndex = rowindex + 1;                Cell Cell4 = rowtemp.createCell(4);                Cell4.setCellFormula("C" + formulaRowIndex + "*D" + formulaRowIndex);                Cell4.setCellStyle(csfield);                //薪水领取时间                Cell Cell5 = rowtemp.createCell(5);                Cell5.setCellValue(emp.getPayTime());                Cell5.setCellStyle(csdate);                rowindex = rowindex + 1;            }            //将内存中的workbook数据写入到流中            wb.write(os);        }    }    @Test    public void ReadExcel() throws Exception {        try (XSSFWorkbook wb = new XSSFWorkbook("tempfile/money.xlsx")) {            Sheet st = wb.getSheet("员工薪水");            XSSFFormulaEvaluator formulaEvaluator = new XSSFFormulaEvaluator(wb);            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");            int rowSeq = 0;            //获取 sheet 中有多少行数据,注意:该 demo 中第一行是标题            Iterator<Row> iterator = st.iterator();            while (iterator.hasNext()) {                Row row = iterator.next();                List<String> list = new ArrayList<>();                if (rowSeq == 0) {                    //读取第一行标题,全是字符串数据                    //一般情况下,你是能够提前知道列数,该 demo 中有 6 列                    for (int i = 0; i < 6; i++) {                        list.add(row.getCell(i).getStringCellValue());                    }                } else {                    //从第二行开始,就是数据了                    //需要注意:必须根据单元格的内容数据类型,使用相应的数据类型读取方法                    //获取序号                    list.add(String.valueOf((int)row.getCell(0).getNumericCellValue()));                    //获取姓名                    list.add(row.getCell(1).getStringCellValue());                    //获取每日薪水                    list.add(String.valueOf((int)row.getCell(2).getNumericCellValue()));                    //获取出勤天数                    list.add(String.valueOf((int)row.getCell(3).getNumericCellValue()));                    //获取薪水统计(注意薪水统计,是由公式计算而来的,也就是要获取公式的值)                    XSSFCell xssfCell = formulaEvaluator.evaluateInCell(row.getCell(4));                    list.add(String.valueOf((int)xssfCell.getNumericCellValue()));                    //获取薪水领取时间                    Date date = row.getCell(5).getDateCellValue();                    list.add(sdf.format(date));                }                //以中文逗号分隔,打印出一行的数据内容                System.out.println(String.join(",", list));                rowSeq = rowSeq + 1;            }        }    }}

以上代码只是简单的演示了 Excel 的常用操作,比如在生成 Excel 文件时,定义不用的单元格样式,在单元格内使用公式的计算结果来填充单元格内容,以及读取 Excel 文件内容时,读取公式计算后的结果。特别需要注意的是:在读取 Excel 单元格的内容时,需要使用对应数据类型的方法来读取,否则就会出异常。

以上代码生成的 Excel 文件的名字为 money.xlsx,存储在项目工程下的 tempfile 目录中,具体内容如下:

读入项目工程目录下的 money.xlsx 文件的内容,展示在控制台上的效果如下图所示:

三、解决控制台 log4j2 报错问题

运行上面 demo 的两个测试方法,在控制台总是显示如下错误提示:

ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...

解决办法分两步:

(1)导入 log4j-core 的 jar 包

<dependency>    <groupId>org.apache.logging.log4j</groupId>    <artifactId>log4j-core</artifactId>    <version>2.17.2</version></dependency>
(2)在 resources 目录下放置 log4j2.xml (日志配置文件)
<Configurationstatus="warn">    <Appenders>        <Consolename="Console"target="SYSTEM_OUT">            <PatternLayoutpattern="[%-5p] %d %c - %m%n"/>        </Console>        <!--日志文件的保存路径,以及日志记录格式-->        <!--        <File name="File" fileName="dist/my.log">            <PatternLayout pattern="%m%n"/>        </File>        -->    </Appenders>    <Loggers>        <Rootlevel="INFO">            <AppenderRefref="Console"/>        </Root>        <!--日志文件记录日志的级别设置-->        <!--        <Logger name="mh.sample2.Log4jTest2" level="INFO">            <AppenderRef ref="File"/>        </Logger>        -->    </Loggers></Configuration>
以上两个步骤完成后,刷新一下 maven 的 jar 包引用,然后再运行两个测试方法后,就没问题了。

相关学习资料