1 Star 0 Fork 165

ElonChung / Java-Review

forked from flatfish / Java-Review 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
Apache-POI-&-EasyExcel.md 17.90 KB
一键复制 编辑 原始数据 按行查看 历史
icanci 提交于 2020-09-07 22:39 . :fire:更新文件名

Apache POI & EasyExcel

常用信息

  • 将用户信息导出为excel表格(导出数据)

  • 将Excel表中的信息录入到网站数据库(习题上传)

开发中经常会设计到Excel的处理,如导出Excel、导入Excel到数据库中

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

Apache POI

Apache POI 官网: http://poi.apache.org/

1594905499738

1594905584312

easyExcel

easyExcel 官网: https://github.com/alibaba/easyexcel

1594905822667

EasyExcel 是阿里巴巴开源的一个excel处理框架,以使用简单,节省内存著称

EasyExcel 能大大减少占用内存的主要原因是因为在解析 Excel 时没有把文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析

内存问题:

  • POI :100w 先加载到内存OOM,在写入文件

  • EasyExcel:小部分消化,一行一行的返回

下图是 EasyExcel 和 POI 在解析Excel时的对比图

1594906026677

官方网址: https://www.yuque.com/easyexcel/doc/easyexcel

POI 操作

POI-Excel写

创建项目

  • 建立一个空项目,创建普通的Maven的 Model

  • 引入pom依赖

    • <!-- xls (03) -->
      <dependency>
          <groupId>org.apache.poi</groupId>
          <artifactId>poi</artifactId>
          <version>4.1.2</version>
      </dependency>
      <!-- xlsx (07) -->
      <dependency>
          <groupId>org.apache.poi</groupId>
          <artifactId>poi-ooxml</artifactId>
          <version>4.1.2</version>
      </dependency>
      <!-- 日期格式化工具 -->
      <dependency>
          <groupId>joda-time</groupId>
          <artifactId>joda-time</artifactId>
          <version>2.9.9</version>
      </dependency>
      <!-- test -->
      <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.12</version>
          <scope>test</scope>
      </dependency>
  • 工作簿

  • 1594906885087

  • 表格中的对象

    • 工作簿
    • 工作表
  • 03版本写文件

  • public static void testWrite03() {
        // 1.创建一个工作簿
        Workbook workbook = new HSSFWorkbook();
        // 2.创建一个工作表
        Sheet sheet = workbook.createSheet("ic 测试表 03");
        // 3.创建一个行
        Row row0 = sheet.createRow(0);
        // 4.创建一个列
        Cell cell11 = row0.createCell(0);
        // 向格子里面写入数据
        cell11.setCellValue("第一行第一列");
        Cell cell12 = row0.createCell(1);
        cell12.setCellValue("第一行第二列");
        Row row1 = sheet.createRow(1);
        Cell cell21 = row1.createCell(0);
        cell21.setCellValue("统计时间");
        Cell cell22 = row1.createCell(1);
        String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        cell22.setCellValue(time);
        // 生成一张表 (IO流)
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(PATH + "\\ic-test03.xls");
            workbook.write(fileOutputStream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("success03");
        }
    }
  • 07版本写文件

  • public static void testWrite07() {
        // 1.创建一个工作簿
        Workbook workbook = new XSSFWorkbook();
        // 2.创建一个工作表
        Sheet sheet = workbook.createSheet("ic 测试表 07");
        // 3.创建一个行
        Row row0 = sheet.createRow(0);
        // 4.创建一个列
        Cell cell11 = row0.createCell(0);
        // 向格子里面写入数据
        cell11.setCellValue("第一行第一列");
        Cell cell12 = row0.createCell(1);
        cell12.setCellValue("第一行第二列");
        Row row1 = sheet.createRow(1);
        Cell cell21 = row1.createCell(0);
        cell21.setCellValue("统计时间");
        Cell cell22 = row1.createCell(1);
        String time = new DateTime().toString("yyyy-MM-dd HH:mm:ss");
        cell22.setCellValue(time);
        // 生成一张表 (IO流)
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(PATH + "\\ic-test07.xls");
            workbook.write(fileOutputStream);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("success07");
        }
    }
  • 测试类

    • package cn.icanci;
      
      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 java.io.FileOutputStream;
      
      public class ExcelWriteTest {
          private static String PATH = "E:\\IdeaHome\\maven\\apache-poi";
      
          public static void main(String[] args) {
              testWrite03();
              testWrite07();
          }
      }
数据批量导入

大文件写HSSF

缺点:最多只能处理65536行,否则会抛出异常

Exception in thread "main" java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535)

优点:存储过程中写入缓存,不操作磁盘,最后一次写入磁盘,速度快

public static void testWrite03BigData() throws Exception {
        // 时间
        long begin = System.currentTimeMillis();
        // 创建一个工作簿
        Workbook workbook = new HSSFWorkbook();
        // 创建一张表
        Sheet sheet = workbook.createSheet("03 版本大数据写");
        // 写入数据
        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);
            }
        }
        System.out.println("over");
        FileOutputStream outputStream = new FileOutputStream(PATH + "\\testWrite03BigData.xls");
        workbook.write(outputStream);
        outputStream.close();
        long end = System.currentTimeMillis();
        System.out.println(end - begin + ":ms");
    }

大文件写XSSF

缺点:写数据时候特别慢,非常消耗内存,也会发生内存溢出,如100w条数据

优点:可以写较大的数据量,如20w条

public static void testWrite07BigData() throws Exception {
    // 时间
    long begin = System.currentTimeMillis();
    // 创建一个工作簿
    Workbook workbook = new XSSFWorkbook();
    // 创建一张表
    Sheet sheet = workbook.createSheet("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);
        }
    }
    System.out.println("over");
    FileOutputStream outputStream = new FileOutputStream(PATH + "\\testWrite07BigData.xlsx");
    workbook.write(outputStream);
    outputStream.close();
    long end = System.currentTimeMillis();
    System.out.println(end - begin + ":ms");
}

大文件写SXSSF

优点:可以写非常大的数据量,如100w条数据甚至更多,写数据速度快,占用更少的内存

注意:

写的过程中会出现临时文件,所以需要清理临时文件

默认有100条记录包保存在内存中,如果超过这个数量,则最前面的数据被写入临时文件

如果想自定义内存中数据的数量,可以使用 new SXSSFWorkbook(数量)

public static void testWrite07BigDataSXSSF() throws Exception {
    // 时间
    long begin = System.currentTimeMillis();
    // 创建一个工作簿
    Workbook workbook = new SXSSFWorkbook();
    // 创建一张表
    Sheet sheet = workbook.createSheet("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);
        }
    }
    System.out.println("over");
    FileOutputStream outputStream = new FileOutputStream(PATH + "\\testWrite07BigDataS.xlsx");
    workbook.write(outputStream);
    outputStream.close();
    // 清除临时文件
    ((SXSSFWorkbook) workbook).dispose();
    long end = System.currentTimeMillis();
    System.out.println(end - begin + ":ms");
}

SXSSFWorkbook - 官方的解释:实现“BigGridDemo”策略的流式XSSFWorkbook版本,这允许写入非常大的文件而不会消耗内存,因为任何时候都只有可配置的行被保存到内存中

请注意,仍然可能消耗大量内存,这些内存基于您正在使用的功能,例如合并区域,注释...仍然值存储在内存中,因此广泛使用,仍然可能需要大量内存

POI-Excel 读

03|07

03版本

public static void testRead03()throws Exception {
    // 获取流
    FileInputStream inputStream = new FileInputStream(PATH + "ic-test03.xls");
    // 1.创建一个工作簿 使用Excel可以操作的,都可以操作
    Workbook workbook = new HSSFWorkbook(inputStream);
    Sheet sheet = workbook.getSheetAt(0);
    Row row = sheet.getRow(0);
    Cell cell = row.getCell(0);
    // 读取值的时候需要注意类型,否则会报错
    // 目前获取的是字符串类型 cell.getStringCellValue()
    System.out.println(cell.getStringCellValue());
    inputStream.close();
}

07版本

public static void testRead07()throws Exception {
    // 获取流
    FileInputStream inputStream = new FileInputStream(PATH + "ic-test07.xlsx");
    // 1.创建一个工作簿 使用Excel可以操作的,都可以操作
    Workbook workbook = new XSSFWorkbook(inputStream);
    Sheet sheet = workbook.getSheetAt(0);
    Row row = sheet.getRow(0);
    Cell cell = row.getCell(0);
    // 读取值的时候需要注意类型,否则会报错
    // 目前获取的是字符串类型 cell.getStringCellValue()
    System.out.println(cell.getStringCellValue());
    inputStream.close();
}

读取不同的数据类型(最麻烦的地方)

public static void testCellType() throws Exception {
    // 获取文件流
    FileInputStream in = new FileInputStream(PATH + "明细表.xlsx");
    // 创建一个工作簿
    Workbook workbook = new XSSFWorkbook(in);
    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();
                System.out.print(cell.getStringCellValue() + " ");
            }
        }
    }
    // 获取表中的内容
    int rowCount = sheet.getPhysicalNumberOfRows();
    for (int rowNum = 0; 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 -1:
                            System.out.print("[ _NONE ]");
                            cellValue = "null";
                            break;
                            // 为 空格
                        case 3:
                            System.out.print("[ BLANK ]");
                            break;
                            // 为String类型
                        case 1:
                            System.out.println("[ STRING ]");
                            cellValue = cell.getStringCellValue();
                            break;
                            // 为 number 类型
                        case 0:
                            // 是一个日期
                            if (HSSFDateUtil.isCellDateFormatted(cell)) {
                                System.out.println("[ Date ]");
                                Date dateCellValue = cell.getDateCellValue();
                                cellValue = new DateTime(dateCellValue).toString("yyyy-MM-dd HH:mm:ss");
                            } else {
                                // 是一个数字
                                System.out.println("[ 转换为字符串显示 ]");
                                cell.setCellType(CellType.STRING);
                                cellValue = cell.toString();
                            }
                            break;
                            // 布尔值
                        case 4:
                            System.out.println("[ 布尔值 ]");
                            cellValue = String.valueOf(cell.getBooleanCellValue());
                            break;
                            // 错误
                        case 5:
                            System.out.println("[ 数据类型错误 ]");
                            break;
                    }
                    System.out.println(cellValue);
                }
            }
        }
    }
    in.close();
}

计算公式(了解即可)

public static void testFormula() throws Exception {
    FileInputStream in = new FileInputStream(PATH + "计算.xlsx");

    Workbook workbook = new XSSFWorkbook(in);
    Sheet sheet = workbook.getSheetAt(0);
    Row row = sheet.getRow(4);
    Cell cell = row.getCell(0);
    // 拿到计算公式
    FormulaEvaluator formulaEvaluator = new XSSFFormulaEvaluator((XSSFWorkbook) workbook);
    // 输出单元格的内容
    int cellType = cell.getCellType();
    switch (cellType) {
            // 是公式
        case 2:
            String cellFormula = cell.getCellFormula();
            System.out.println(cellFormula);
            CellValue evaluate = formulaEvaluator.evaluate(cell);
            String result = evaluate.formatAsString();
            System.out.println(result);
            break;
    }
    in.close();
}

EasyExcel 操作

官方文档: https://www.yuque.com/easyexcel/doc

Github案例: https://github.com/alibaba/easyexcel

导入依赖

<!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.6</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.25</version>
</dependency>

写入测试

DemoData.java

@Data
public class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;
}

测试写入数据

package cn.icanci;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;

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

/**
 * @Author: icanci
 * @ProjectName: apache-poi
 * @PackageName: cn.icanci
 * @Date: Created in 2020/7/17 8:42
 * @ClassAction:
 */
public class TestDemo {
    private static String PATH = "E:\\IdeaHome\\maven\\apache-poi\\";

    public static void main(String[] args) {
        new TestDemo().simpleWrite();
    }

    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
     */
    public void simpleWrite() {
        // 写法1
        String fileName =PATH+ "simpleWrite1.xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可

        // write (fileName,格式类)
        // sheet (表名)
        // doWrite(数据)  真实情况应该是从数据库读取的数据,或者是用户上传的数据
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());

        // 写法2
        fileName = PATH + "simpleWrite2.xlsx";
        // 这里 需要指定写用哪个class去写
        ExcelWriter excelWriter = null;
        try {
            excelWriter = EasyExcel.write(fileName, DemoData.class).build();
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            excelWriter.write(data(), writeSheet);
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }
}

最终的结果

读取测试

官方文档: https://www.yuque.com/easyexcel/doc

Github案例: https://github.com/alibaba/easyexcel

固定套路

  • 写入,固定类格式进行写入
  • 读取,根据监听器设置的规则进行读取

总结

面对对象的方法、思想,学会面向接口编程

1
https://gitee.com/elonchung/Java-Review.git
git@gitee.com:elonchung/Java-Review.git
elonchung
Java-Review
Java-Review
master

搜索帮助