POI及EasyExcel使用


★應用場景

  1.將用戶信息導出為excel表格(導出數據...)

  2.將excel表中的信息錄入到網站數據庫

  操作excel目前比較流行的就是Apache POI 和阿里巴巴的easyExcel

Apache POI

  官網:https://poi.apache.org/    使用起來相對比較麻煩,消耗內存

  基本功能

    HSSF:提供讀寫excel格式檔案的功能(03版本,行數最多65536行)

    XSSF:提供讀寫excel、ooxml格式檔案的功能(07版本。l理論 行數沒有限制)

    HWPF:提供讀寫我認得格式檔案的功能

    HSLF:提供讀寫PowerPoint格式檔案的功能

    HDGF:提供讀寫Visio格式檔案的功能

easyExcel 

  官網:https://github.com/alibaba/easyexcel

  在poi基礎上優化,降低內存使用,開發使用更簡單。

POI和easyExcel區別

  POI:一次性將數據加載到內存,可能出現內存溢出。oom問題。

  easyExcel:一次一行操作。

POI-Excel寫

1.創建一個空的Maven項目

2.引入xls(03版本)和xlsx(07版本)依賴

<dependencies>
<!--xls(03)-->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<!--xlsx(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>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>

 

 

 步驟1:工作簿  -->  步驟2:工作表  -->  步驟3:行  -->  步驟4:列

package com.ckfuture;

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.joda.time.DateTime;
import org.junit.Test;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.security.spec.RSAOtherPrimeInfo;

public class ExcelWriteText {

    String PATH="H:\\javaProject\\javaExcel";
    @Test
   public void testWrite03() throws Exception {
       //1.創建一個工作薄
       Workbook workbook=new HSSFWorkbook();
       //2.創建一個工作表
       Sheet sheet=workbook.createSheet("創客未來統計表");
        //3.創建一個行
       Row row1=sheet.createRow(0);//第一行
       //4.創建一個單元格
       Cell cell11 = row1.createCell(0);//第一列
       cell11.setCellValue("今日的新增觀眾");

       Cell cell12 = row1.createCell(1);//第二列
       cell12.setCellValue(777);

       //第二行
       Row row2=sheet.createRow(1);//第二行
       Cell cell21 = row2.createCell(0);//第一列
       cell21.setCellValue("統計時間");
       Cell cell22 = row2.createCell(1);//第二列
       String time=new DateTime().toString("yyyy-MM-dd HH:mm:ss");
       cell22.setCellValue(time);

       //生成一張表(IO流)
       FileOutputStream fileOutputStream = new FileOutputStream(PATH + "/創客未來03.xls");
       //輸出
       workbook.write(fileOutputStream);
       //關閉流
       fileOutputStream.close();
       System.out.println("創客未來03 生成完畢!");
   }
}

 

@Test
    public void testWrite07() throws Exception {        //1.創建一個工作薄
        Workbook workbook=new XSSFWorkbook();
        //2.創建一個工作表
        Sheet sheet=workbook.createSheet("創客未來統計表");
        //3.創建一個行
        Row row1=sheet.createRow(0);//第一行
        //4.創建一個單元格
        Cell cell11 = row1.createCell(0);//第一列
        cell11.setCellValue("今日的新增觀眾");

        Cell cell12 = row1.createCell(1);//第二列
        cell12.setCellValue(777);

        //第二行
        Row row2=sheet.createRow(1);//第二行
        Cell cell21 = row2.createCell(0);//第一列
        cell21.setCellValue("統計時間");
        Cell cell22 = row2.createCell(1);//第二列
        String time=new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        cell22.setCellValue(time);

        //生成一張表(IO流)
        FileOutputStream fileOutputStream = new FileOutputStream(PATH + "/創客未來07.xlsx");
        //輸出
        workbook.write(fileOutputStream);
        //關閉流
        fileOutputStream.close();
        System.out.println("創客未來07 生成完畢!");
    }

 

注意對象的一個區別,文件后綴!

數據批量導入

03版本批量導入

    @Test
    public void testWrite03BigData() throws Exception {
        //時間
        long begin = System.currentTimeMillis();
        //創建一個工作簿
        Workbook workbook = new HSSFWorkbook();
        //創建一個表
        Sheet sheet = workbook.createSheet();
        //寫入數據
        for (int rowNum = 0; rowNum < 65536; rowNum++) {
            Row row = sheet.createRow(rowNum);//每一行
            for (int cellNum = 0; cellNum < 10; cellNum++) {
                row.createCell(cellNum).setCellValue(cellNum);//每一列
            }
        }
        System.out.println("write over");
        FileOutputStream outputStream = new FileOutputStream(PATH + "/testWrite03BigData.xls");
        workbook.write(outputStream);
        outputStream.close();

        long end = System.currentTimeMillis();
        System.out.println((double)(end-begin)/1000);
    }

 

 

 07版本批量導入

@Test
    public void testWrite07BigData() throws Exception {
        //時間
        long begin = System.currentTimeMillis();
        //創建一個工作簿
        Workbook workbook = new XSSFWorkbook();
        //創建一個表
        Sheet sheet = workbook.createSheet();
        //寫入數據
        for (int rowNum = 0; rowNum < 65537; rowNum++) {
            Row row = sheet.createRow(rowNum);//每一行
            for (int cellNum = 0; cellNum < 10; cellNum++) {
                row.createCell(cellNum).setCellValue(cellNum);//每一列
            }
        }
        System.out.println("write over");
        FileOutputStream outputStream = new FileOutputStream(PATH + "/testWrite07BigData.xlsx");
        workbook.write(outputStream);
        outputStream.close();

        long end = System.currentTimeMillis();
        System.out.println((double)(end-begin)/1000);
    }

 

 

 07版本耗時比較長。如果優化可以才用 SXXFF

 @Test
    public void testWrite07BigDataS() throws Exception {
        //時間
        long begin = System.currentTimeMillis();
        //創建一個工作簿
        Workbook workbook = new SXSSFWorkbook();
        //創建一個表
        Sheet sheet = workbook.createSheet();
        //寫入數據
        for (int rowNum = 0; rowNum < 65537; rowNum++) {
            Row row = sheet.createRow(rowNum);//每一行
            for (int cellNum = 0; cellNum < 10; cellNum++) {
                row.createCell(cellNum).setCellValue(cellNum);//每一列
            }
        }
        System.out.println("write over");
        FileOutputStream outputStream = new FileOutputStream(PATH + "/testWrite07BigDataS.xlsx");
        workbook.write(outputStream);
        outputStream.close();
        //清除臨時文件
        ((SXSSFWorkbook)workbook).dispose();
        long end = System.currentTimeMillis();
        System.out.println((double)(end-begin)/1000);
    }

 

 

 SXSSFWorkbook 允許寫入非常大的文件而不會耗盡內存,但仍然會消耗大量內存,如果廣泛使用需要大量內存。

POI-Excel讀

1.簡單讀取,讀取的excel文件數據類型是相同

03版本讀取
package com.ckfuture;

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.joda.time.DateTime;
import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class ExcelReadText {
String PATH="H:\\javaProject\\javaExcel";
@Test
public void testRead03() throws Exception {
//獲取文件流
FileInputStream inputStream = new FileInputStream(PATH + "/創客未來03.xls");

//1.創建一個工作薄,使用excel能操作的這邊它都可以操作
Workbook workbook=new HSSFWorkbook(inputStream);
//2.得到表
Sheet sheetAt = workbook.getSheetAt(0);
//3.得到行
Row row = sheetAt.getRow(0);//獲取第一行
//4.得到列
Cell cell = row.getCell(0);//獲取第一列
//讀取值的時候,一定需要注意類型
//getStringCellValue 字符串類型
System.out.println(cell.getStringCellValue());
//關閉流
inputStream.close();
}
}

 

 

 07版本讀取

 @Test
    public void testRead07() throws Exception {
        //獲取文件流
        FileInputStream inputStream = new FileInputStream(PATH + "/創客未來07.xlsx");

        //1.創建一個工作薄,使用excel能操作的這邊它都可以操作
        Workbook workbook=new XSSFWorkbook(inputStream);
        //2.得到表
        Sheet sheetAt = workbook.getSheetAt(0);
        //3.得到行
        Row row = sheetAt.getRow(0);//獲取第一行
        //4.得到列
        Cell cell = row.getCell(0);//獲取第一列
        //讀取值的時候,一定需要注意類型
        //getStringCellValue 字符串類型
        System.out.println(cell.getStringCellValue());
        //關閉流
        inputStream.close();
    }

 

 

 2.讀取不同類型的數據

  @Test
    public void testCellType() throws Exception {
        //獲取文件流
        FileInputStream inputStream = new FileInputStream(PATH + "/創客未來03.xls");
        //1.創建一個工作薄,使用excel能操作的這邊它都可以操作
        Workbook workbook=new HSSFWorkbook(inputStream);
        Sheet sheet = workbook.getSheetAt(0);
        //獲取標題內容
        Row rowTitle = sheet.getRow(0);
        if(rowTitle!=null){
            //獲取一行中有多少列是存在數據
            int cellCount = rowTitle.getPhysicalNumberOfCells();
            for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                Cell cell = rowTitle.getCell(cellNum);
                if(cell!=null){
                    int cellType = cell.getCellType();
                    String cellValue = cell.getStringCellValue();
                    System.out.println(cellValue);
                }
            }
            System.out.println();
        }
        //獲取表中的內容
        int rowCount = sheet.getPhysicalNumberOfRows();//獲取excel表格中的行數
        for (int rowNum = 1; rowNum < rowCount; rowNum++) {
            Row rowData = sheet.getRow(rowNum);
            if(rowData!=null){
                //讀取列
                int cellCount = rowTitle.getPhysicalNumberOfCells();
                for (int cellNum = 0; cellNum < cellCount; cellNum++) {
                    System.out.print("["+(rowNum+1)+"-"+(cellNum+1)+"]");

                    Cell cell = rowData.getCell(cellNum);
                    //匹配列的數據類型,寫到吐!
                    if(cell!=null){
                        int cellType = cell.getCellType();
                        String cellValue="";
                        switch (cellType){
                            case HSSFCell.CELL_TYPE_STRING://字符串
                                cellValue=cell.getStringCellValue();
                                break;
                            case HSSFCell.CELL_TYPE_BOOLEAN://布爾
                                cellValue=String.valueOf(cell.getBooleanCellValue());
                                break;
                            case HSSFCell.CELL_TYPE_BLANK:////空值不需要返回
                                break;
                            case HSSFCell.CELL_TYPE_NUMERIC://數字(日期、普通數字)
                                if(HSSFDateUtil.isCellDateFormatted(cell)){//日期
                                    Date date = cell.getDateCellValue();
                                    cellValue= new DateTime(date).toString("yyyy-MM-dd");
                                }else {//普通數字
                                    //不是日期格式,防止數字過長!
                                    cell.setCellType(HSSFCell.CELL_TYPE_STRING);
                                    cellValue=cell.toString();
                                }
                                break;
                            case HSSFCell.CELL_TYPE_ERROR://錯誤
                                //數據類型錯誤不需要返回
                                break;
                        }
                        System.out.println(cellValue);
                    }
                }
            }
        }
        //關閉流
        inputStream.close();
    }

 

 

 3.讀取計算公式

@Test
    public  void testFormula() throws Exception {
        FileInputStream inputStream = new FileInputStream(PATH + "/公式.xls");
        Workbook workbook = new HSSFWorkbook(inputStream);
        Sheet sheet = workbook.getSheetAt(0);
        Row row = sheet.getRow(3);
        Cell cell = row.getCell(0);
        //拿到計算公式 eval
        FormulaEvaluator FormulaEvaluator = new HSSFFormulaEvaluator((HSSFWorkbook) workbook);
        //輸出單元格的內容
        int cellType = cell.getCellType();
        switch(cellType){
            case Cell.CELL_TYPE_FORMULA://公式
                String formula = cell.getCellFormula();
                System.out.println(formula);
                //計算
                CellValue evaluate = FormulaEvaluator.evaluate(cell);
                String cellValue = evaluate.formatAsString();
                System.out.println(cellValue);
                break;
        }

    }

★EasyExcel操作(alibaba開源項目)

1.導入依賴

<!--導入easyExcel-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>2.2.0-beta2</version>
    </dependency>

2.建立一個實體類(easyexcel的寫入規則)

package com.ckfuture.easy;
import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.util.Date;

@Data
public class DemoData {
    @ExcelProperty("字符串標題")
    private String string;
    @ExcelProperty("日期標題")
    private Date date;
    @ExcelProperty("數字標題")
    private Double doubleData;

    @ExcelIgnore
    private String ignore;

    public void setString(String s) {
        this.string=s;
    }

    public void setDate(Date date) {
        this.date=date;
    }

    public void setDoubleData(double v) {
        this.doubleData=v;
    }
}

3.建立測試類  EasyTest 寫操作

package com.ckfuture.easy;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class EasyTest {
String PATH="H:\\javaProject\\javaExcel";

private List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
}
return list;
}
//根據list 寫入excel
@Test
public void simpleWrite(){
//寫法1
String fileName=PATH+"/EasyTest.xlsx";
//這里需要指定寫用哪個class去寫,然后寫到第一給sheet,名字為模板 然后文件流會自動關閉
//write(fileName,格式類)
//sheet(表名)
//doWrite(數據)
EasyExcel.write(fileName,DemoData.class).sheet("模板").doWrite(data());

//寫法2
// fileName=PATH+"/EasyTest2.xlsx";
// ExcelWriter excelWriter=EasyExcel.write(fileName,DemoData.class).build();
// WriteSheet writeSheet=EasyExcel.writerSheet("模板").build();
// excelWriter.write(data(),writeSheet);
// //finish幫忙關閉流
// excelWriter.finish();
}
}

 

 

 

 4.讀操作 首先創建 監聽器

package com.ckfuture.easy;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

// 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然后里面用到spring可以構造方法傳進去
public class DemoDataListener extends AnalysisEventListener<DemoData> {
    private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);

    private static final int BATCH_COUNT = 5;
    List<DemoData> list = new ArrayList<DemoData>();

    private DemoDAO demoDAO;
    public DemoDataListener() {
        // 這里是demo,所以隨便new一個。實際使用如果到了spring,請使用下面的有參構造函數
        demoDAO = new DemoDAO();
    }

    public DemoDataListener(DemoDAO demoDAO) {
        this.demoDAO = demoDAO;
    }

    // 讀取數據會執行 invoke 方法
    // DemoData 類型
    // AnalysisContext 分析上問
    @Override
    public void invoke(DemoData data, AnalysisContext context) {
        System.out.println(JSON.toJSONString(data));
        list.add(data);
        // 達到BATCH_COUNT了,需要去存儲一次數據庫,防止數據幾萬條數據在內存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            saveData(); // 持久化邏輯!
            // 存儲完成清理 list
            list.clear();
        }
    }
    /**
     * 所有數據解析完成了 都會來調用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 這里也要保存數據,確保最后遺留的數據也存儲到數據庫
        saveData();
        LOGGER.info("所有數據解析完成!");
    }
    /**
     * 加上存儲數據庫
     */
    private void saveData() {
        LOGGER.info("{}條數據,開始存儲數據庫!", list.size());
        demoDAO.save(list);
        LOGGER.info("存儲數據庫成功!");
    }
}

5.讀操作 再創建持久化層用於插入數據庫

package com.ckfuture.easy;

import java.util.List;

public class DemoDAO {
    public void save(List<DemoData> list) {
        // 持久化操作!
        // 如果是mybatis,盡量別直接調用多次insert,自己寫一個mapper里面新增一個方法batchInsert,所有數據一次性插入
    }
}

6.讀操作 測試

 @Test
    public void simpleRead() {
        // 有個很重要的點 DemoDataListener 不能被spring管理,要每次讀取excel都要new,然后里面用到spring可以構造方法傳進去
        // 寫法1:
        String fileName = PATH + "/EasyTest.xlsx";
        // 這里 需要指定讀用哪個class去讀,然后讀取第一個sheet 文件流會自動關閉

        // 重點注意讀取的邏輯 DemoDataListener
        EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
    }


免責聲明!

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



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