Java 操做 Excel

Java 操做 Excel

還記得IO裏說過一句話嗎?一切皆字節,excel的xls固然也能夠用IO流讀寫。java

但至關的麻煩,因此須要藉助第三方接口、組件快速操做Excel!git

操做Excel目前比較流行的就是Apache 的POI 和 阿里巴巴的easyExcelgithub

經常使用場景

  1. 將用戶信息導出爲excel表格(導出數據)
  2. 將Excel表中的信息錄入到網站數據庫(數據上傳)

POI

什麼是POI ?數據庫

Apache POI 是Apache軟件基金會的開放源碼函式庫,POI提供API給Java程序對Microsoft Office格式檔案讀和寫的功能。apache

image-20200521151259812

原生的源碼庫使用起來會比較麻煩,因而出現了easyExcel編程

POI官網緩存

實例測試

  1. 首先建立一個空項目框架

    image-20200521155943768

  2. 在項目結構(快捷鍵Ctrl + Shift + Alt + s)中選擇合適的SDKxss

    image-20200521160209632

  3. 在Modules模塊添加POI項目工具

    image-20200521160336930

    image-20200521160441745

    下一步完成項目建立!

  4. 在pom.xml中導入依賴

    <!-- 導入依賴-->
        <dependencies>
            <!--xls(03)-->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>3.9</version>
            </dependency>
            <!--xls(07)-->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>3.9</version>
            </dependency>
            <!--日期格式化工具-->
            <dependency>
                <groupId>joda-time</groupId>
                <artifactId>joda-time</artifactId>
                <version>2.10.1</version>
            </dependency>
            <!--test-->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
            </dependency>
        </dependencies>

    image-20200521160706595

    image-20200521160911682

    導入依賴時會顯示不少紅色錯誤,稍等幾分鐘等待IDEA自動下載依賴後就不報錯了。

  5. 編寫相關代碼

POI-Excel寫數據

package com.poi;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.joda.time.DateTime;
import org.junit.Test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class ExcelWriteTest {

    @Test
    public void testWirte01() throws IOException {
        // 1.建立工做簿 HSSF是03版本
        Workbook workbook = new HSSFWorkbook();
        // 2.經過工做薄 -> 建立工做表
        Sheet sheet = workbook.createSheet("成績表");
        // 3.經過工做表 -> 建立行
        Row row1 = sheet.createRow(0);
        // 4.經過行 -> 建立單元格(A1) 並填充數據
        Cell cell = row1.createCell(0);
        cell.setCellValue("張三");

        Cell cell2 = row1.createCell(1);
        cell2.setCellValue(99);

        Cell cell3 = row1.createCell(2);
        String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        cell3.setCellValue(time);


        // 保存文件
        File file = new File(".\\成績表.xls");
        FileOutputStream fos = null;
        try{
            fos = new FileOutputStream(file);
            workbook.write(fos);
            System.out.println("完成");

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                fos.close();
            }
        }

    }


    @Test
    public void testWirte02(){
        // 1.建立工做簿 XSSF是07版本
        Workbook workbook = new XSSFWorkbook();
        // 2.經過工做薄 -> 建立工做表
        Sheet sheet = workbook.createSheet("成績表");
        // 3.經過工做表 -> 建立行
        Row row1 = sheet.createRow(0);
        // 4.經過行 -> 建立單元格(A1) 並填充數據
        Cell cell = row1.createCell(0);
        cell.setCellValue("張三");

        Cell cell2 = row1.createCell(1);
        cell2.setCellValue(99);

        Cell cell3 = row1.createCell(2);
        String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        cell3.setCellValue(time);


        // 保存文件
        File file = new File(".\\成績表2.xlsx");
        FileOutputStream fos = null;
        try{
            fos = new FileOutputStream(file);
            workbook.write(fos);
            System.out.println("完成");

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

面向接口編程的好處, testWirte02只將HSSFWorkbook()修改爲XSSFWorkbook() 其餘的大量代碼相同。

還有最後保存文件時,注意兩個版本的文件後綴是不同的

  • 2003版 後綴 .xls 最大65536行
  • 2007版 後綴 .xlsx 無上限

數據批量導入

大文件使用HSSF (03)
  • 缺點:最多隻能處理65536行,不然拋出異常
  • 優勢:速度快!過程寫入緩存,不操做硬盤,最後一次性寫入磁盤
@Test
public void testWirte01() throws IOException {
    // 開始時間
    long s = System.currentTimeMillis();
    // 1.建立工做簿 HSSF是03版本
    Workbook workbook = new HSSFWorkbook();
    // 2.經過工做薄 -> 建立工做表
    Sheet sheet = workbook.createSheet("測試表");
    // 3.大量數據寫入 03版一旦rowNum超過65536就會報錯
    for (int rowNum = 0; rowNum < 65536; rowNum++) {
        Row row = sheet.createRow(rowNum);
        for (int cellNum = 0; cellNum < 10; cellNum++) {
            Cell cell = row.createCell(cellNum);
            cell.setCellValue(cellNum+1);
        }
    }

    // 保存文件
    File file = new File(".\\03版填滿.xls");
    FileOutputStream fos = null;
    try{
        fos = new FileOutputStream(file);
        workbook.write(fos);
        System.out.println("完成");

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        if (fos != null) {
            fos.close();
        }
    }
    long e = System.currentTimeMillis();
    System.out.println((double)(e-s)/1000);
}
大文件使用XSSF (07)
  • 缺點:很是慢!並且耗內存!也存在內存溢出,如上百萬條
  • 優勢:能夠寫超過65536行的數據量,如20萬條
@Test
public void testWirte02() throws IOException {
    // 開始時間
    long s = System.currentTimeMillis();
    // 1.建立工做簿 HSSF是03版本
    Workbook workbook = new XSSFWorkbook();
    // 2.經過工做薄 -> 建立工做表
    Sheet sheet = workbook.createSheet("測試表");
    // 3.大量數據寫入 XSSF 07版不限制行數
    for (int rowNum = 0; rowNum < 100000; rowNum++) {
        Row row = sheet.createRow(rowNum);
        for (int cellNum = 0; cellNum < 10; cellNum++) {
            Cell cell = row.createCell(cellNum);
            cell.setCellValue(cellNum+1);
        }
    }

    // 保存文件
    File file = new File(".\\07版填滿.xlsx");
    FileOutputStream fos = null;
    try{
        fos = new FileOutputStream(file);
        workbook.write(fos);
        System.out.println("完成");

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        if (fos != null) {
            fos.close();
        }
    }
    long e = System.currentTimeMillis();
    System.out.println((double)(e-s)/1000);
}
XSSF 優化接口 SXSSF
@Test
public void testWirte03() throws IOException {
    // 開始時間
    long s = System.currentTimeMillis();
    // 1.建立工做簿 HSSF是03版本
    Workbook workbook = new SXSSFWorkbook();
    // 2.經過工做薄 -> 建立工做表
    Sheet sheet = workbook.createSheet("測試表");
    // 3.大量數據寫入 XSSF 07版不限制行數
    for (int rowNum = 0; rowNum < 100000; rowNum++) {
        Row row = sheet.createRow(rowNum);
        for (int cellNum = 0; cellNum < 10; cellNum++) {
            Cell cell = row.createCell(cellNum);
            cell.setCellValue(cellNum+1);
        }
    }

    // 保存文件
    File file = new File(".\\07版填滿增強.xlsx");
    FileOutputStream fos = null;
    try{
        fos = new FileOutputStream(file);
        workbook.write(fos);
        System.out.println("完成");

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } finally {
        if (fos != null) {
            fos.close();
        }
    }
    // 清理臨時文件
    ((SXSSFWorkbook) workbook).dispose();
    long e = System.currentTimeMillis();
    System.out.println((double)(e-s)/1000);
}

SXSSF又快又能夠寫入大量數據

POI-Excel讀數據

package com.poi;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.junit.Test;

import java.io.FileInputStream;

public class ExcelReadTest {
    @Test
    public void testRead03() throws Exception {
        // 獲取文件流
        FileInputStream fis = new FileInputStream("成績表.xls");
        // 建立工做簿 03版本
        Workbook workbook = new HSSFWorkbook(fis);
        // 獲取工做表
        Sheet sheet = workbook.getSheetAt(0);
        // 獲取行
        Row row = sheet.getRow(0);
        // 獲取單元格
        Cell cell = row.getCell(0);
        // 獲取單元格的內容
        String value = cell.getStringCellValue();
        System.out.println(value);
        fis.close();
    }

    @Test
    public void testRead07() throws Exception {
        // 獲取文件流
        FileInputStream fis = new FileInputStream("成績表2.xlsx");
        // 建立工做簿 07版本
        Workbook workbook = new XSSFWorkbook(fis);
        // 獲取工做表
        Sheet sheet = workbook.getSheetAt(0);
        // 獲取行
        Row row = sheet.getRow(0);
        // 獲取單元格
        Cell cell = row.getCell(0);
        // 獲取單元格的內容
        String value = cell.getStringCellValue();
        System.out.println(value);
        fis.close();
    }
}

注意

test.xls

學號 姓名 課程 分數
17003009 張三 高數 95
17003008 李四 計網 90
17003007 王五 大英 75
260

獲取單元格的公式(瞭解)

上表格中分數彙總的260是=SUM(D2:D4) 獲得的

@Test
public void testFormula() throws Exception {
    // 首先拿到文件
    FileInputStream fis = new FileInputStream(".\\test.xls");
    // 獲取工做牌
    Workbook workbook = new HSSFWorkbook(fis);
    // 獲取工做表
    Sheet sheet = workbook.getSheetAt(0);
    // 定位單元格
    Row row = sheet.getRow(4);
    Cell cell = row.getCell(3);

    // 直接獲取單元格的值
    double cellValue = cell.getNumericCellValue();
    System.out.println("單元格的值:" + cellValue);

    // 建立公式類實例
    FormulaEvaluator formulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook) workbook);

    //輸出單元格公式之內容
    int cellType = cell.getCellType();
    switch (cellType) {
        case CELL_TYPE_FORMULA:
            // 拿到計算公式
            String formula = cell.getCellFormula();
            System.out.println("單元格的公式:" + formula);
            // 統計計算器計算單元格的值
            CellValue value = formulaEvaluator.evaluate(cell);
            // 打印的結果
            String s = value.formatAsString();
            System.out.println("公式算來的值:" + s);
            break;
        default:
            break;
    }

    fis.close();
}

運行結果

單元格的值:260.0
單元格的公式:SUM(D2:D4)
公式算來的值:260.0

封裝成的工具類

package com.poi;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFFormulaEvaluator;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.joda.time.DateTime;
import org.junit.Test;

import java.io.FileInputStream;
import java.util.Date;

import static org.apache.poi.ss.usermodel.Cell.*;

public class ExcelPOIUtil {
    @Test
    public void testCellType() throws Exception {
        // 獲取文件流 改造工具類是把下面這行寫到方法參數,經過傳路徑實現讀表
        FileInputStream fis = new FileInputStream("test.xls");
        // 建立工做簿
        Workbook workbook = new HSSFWorkbook(fis);
        // 獲取工做表
        Sheet sheet = workbook.getSheetAt(0);
        // 讀取第一行 屬性字段
        Row rowTitle = sheet.getRow(0);
        if (rowTitle != null) {
            int cells = rowTitle.getPhysicalNumberOfCells();
            for (int i = 0; i < cells; i++) {
                // 獲取單元格
                Cell cell = rowTitle.getCell(i);
                if (cell != null) {
                    int type = cell.getCellType();
                    String s = cell.getStringCellValue();
                    System.out.print(s + "\t");
                }
            }
        }
        System.out.println();
        int rows = sheet.getPhysicalNumberOfRows();
        for (int i = 1; i < rows; i++) {
            Row row = sheet.getRow(i);
            if (row != null) {
                int cells = row.getPhysicalNumberOfCells();
                for (int j = 0; j < cells; j++) {
                    System.out.print("(" + (char) ('A' + i) + "" + (j + 1) + ") ");
                    Cell cell = row.getCell(j);
                    // 匹配類型!!難點
                    if (cell != null) {
                        // 首先獲取單元格數據類型,可是以數字表示
                        int type = cell.getCellType();
                        System.out.print(type);
                        String value = "";
                        // 須要switch匹配相應的類型
                        switch (type) {
                            case CELL_TYPE_STRING:
                                System.out.print("【String】");
                                value = cell.getStringCellValue();
                                break;
                            case CELL_TYPE_BOOLEAN:
                                System.out.print("【Boolean】");
                                value = String.valueOf(cell.getBooleanCellValue());
                                break;
                            case CELL_TYPE_NUMERIC:
                                if (HSSFDateUtil.isCellDateFormatted(cell)) {
                                    System.out.print("【日期】");
                                    Date data = cell.getDateCellValue();
                                    value = new DateTime(data).toString("yyyy-HH-mm HH:mm:ss");

                                } else {
                                    System.out.print("【數字串】");
                                    cell.setCellType(CELL_TYPE_STRING);
                                    value = cell.toString();

                                }
                                break;

                            case CELL_TYPE_ERROR:
                                System.out.print("【Error】");
                                break;
                            case CELL_TYPE_BLANK:
                                System.out.print("【Blank】");
                                break;
                            default:
                                break;
                        }
                        System.out.println(value);
                    }
                }

            }
        }

        fis.close();
    }
}

運行結果

學號	姓名	課程	分數	
(B1) 0【數字串】17003009
(B2) 1【String】張三
(B3) 1【String】高數
(B4) 0【數字串】95
(C1) 0【數字串】17003008
(C2) 1【String】李四
(C3) 1【String】計網
(C4) 0【數字串】90
(D1) 0【數字串】17003007
(D2) 1【String】王五
(D3) 1【String】大英
(D4) 0【數字串】75
(E1) 1【String】 
(E2) 1【String】 
(E3) 3【Blank】
(E4) 2【Formula】公式=SUM(D2:D4) 結果:0.0

雙手撓頭,爲何獲取公式單獨測試時可以準確計算出公式的結果260,將該模塊添加到工具類以後,結果又除了問題。

查了好多資料沒找到緣由,去問問狂老師,等後續解惑以後再上來更新。

easyExcel

easyExcel是什麼?

快速、簡單避免OOM的java處理Excel工具 。

Java解析、生成Excel比較有名的框架有Apache poi、jxl。

但他們都存在一個嚴重的問題就是很是的耗內存,poi有一套SAX模式的API能夠必定程度的解決一些內存溢出的問題,

但POI仍是有一些缺陷,好比07版Excel解壓縮以及解壓後存儲都是在內存中完成的,內存消耗依然很大。

easyexcel重寫了poi對07版Excel的解析,可以本來一個3M的excel用POI sax依然須要100M左右內存下降到幾M,

而且再大的excel不會出現內存溢出,03版依賴POI的sax模式。在上層作了模型轉換的封裝,讓使用者更加簡單方便

easyExcel官網

easyExcel官方文檔

image-20200521151929866

首先從easyExcel項目上拷貝pom.xml的依賴

<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.3</version>

image-20200522113100071

小結-學習方式

瞭解面向對象的思想,學會面向接口編程。

理解使用測試API

視頻教程Bilibili:碰見狂神說 關注公衆號:狂神說
做業:後期把easyExcel官方文檔的API都測試一下,再補充上面easyExcel知識。

相關文章
相關標籤/搜索