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 包文件。

搭建好的項目工程整體目錄比較簡單,具體如下圖所示:

image

項目工程結構簡單介紹:

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 目錄中,具體內容如下:

image

讀入項目工程目錄下的 money.xlsx 文件的內容,展示在控制台上的效果如下圖所示:

image


三、解決控制台 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 (日志配置文件)

<Configuration status="warn">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="[%-5p] %d %c - %m%n"/>
        </Console>

        <!--日志文件的保存路徑,以及日志記錄格式-->
        <!--
        <File name="File" fileName="dist/my.log">
            <PatternLayout pattern="%m%n"/>
        </File>
        -->
    </Appenders>
    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="Console"/>
        </Root>

        <!--日志文件記錄日志的級別設置-->
        <!--
        <Logger name="mh.sample2.Log4jTest2" level="INFO">
            <AppenderRef ref="File"/>
        </Logger>
        -->
    </Loggers>
</Configuration>

以上兩個步驟完成后,刷新一下 maven 的 jar 包引用,然后再運行兩個測試方法后,就沒問題了。


本 demo 示例的源代碼下載地址為:https://files.cnblogs.com/files/blogs/699532/poiTest.zip




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM