• JAVA-POI && easyEXCEL


    https://easyexcel.opensource.alibaba.com/docs/current/quickstart/write#web%E4%B8%AD%E7%9A%84%E5%86%99

    Apache POI [1] 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。

    HSSF [1]  - 提供读写Microsoft Excel XLS格式档案的功能。
    XSSF [1]  - 提供读写Microsoft Excel OOXML XLSX格式档案的功能。
    HWPF [1]  - 提供读写Microsoft Word DOC格式档案的功能。
    HSLF [1]  - 提供读写Microsoft PowerPoint格式档案的功能。
    HDGF [1]  - 提供读Microsoft Visio格式档案的功能。
    HPBF [1]  - 提供读Microsoft Publisher格式档案的功能。
    HSMF [1]  - 提供读Microsoft Outlook格式档案的功能。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    Java领域解析、生成Excel比较有名的框架有Apache poi、jxl等。但他们都存在一个严重的问题就是非常的耗内存。如果你的系统并发量不大的话可能还行,但是一旦并发上来后一定会OOM或者JVM频繁的full gc。easyExcel是阿里巴巴开源的一个excel处理框架,以使用简单、节省内存著称。64M内存1分钟内读取75M(46W行25列)的Excel(当然还有急速模式能更快,但是内存占用会在100M多一点)。

    在这里插入图片描述
    easyExcel能大大减少占用内存的主要原因是在解析Excel时没有将文件数据一次性全部加载到内存中,而是从磁盘上一行行读取数据,逐个解析。下图是easyExcel和POI在解析Excel时的对比图。
    在这里插入图片描述
    easyExcel采用一行一行的解析模式,并将一行的解析结果以观察者的模式通知处理(AnalysisEventListener)。

    1 基础版本

    <!-- https://mvnrepository.com/artifact/org.apache.poi/poi 03 -->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi</artifactId>
                <version>3.9</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml 07-->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>3.9</version>
            </dependency>
            <!-- https://mvnrepository.com/artifact/joda-time/joda-time -->
            <dependency>
                <groupId>joda-time</groupId>
                <artifactId>joda-time</artifactId>
                <version>2.10.1</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在这里插入图片描述

    在这里插入图片描述
    HSSFWorkbook 03
    XSSFWorkbook 07
    SXSSFWorkbook 超级

    package com.poi;
    
    import cn.hutool.core.date.DateTime;
    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 java.io.FileOutputStream;
    import java.io.IOException;
    
    public class POITest {
        public static void main(String[] args) throws IOException {
            test03();
            test07();
        }
        
        public static void test07() throws IOException {
            // 创建一个工作簿
            Workbook workbook = new XSSFWorkbook();
            // 创建一个工作表
            Sheet sheet = workbook.createSheet("sheet-1");
            // 创建一行
            Row row_0 = sheet.createRow(0); // 第一行
    
            // 创建一个单元格
            Cell cell_00 = row_0.createCell(0);
            cell_00.setCellValue("今日新增观众"); // (0,0)
            Cell cell_01 = row_0.createCell(1);
            cell_01.setCellValue(666); // (0,1)
    
            Row row_1 = sheet.createRow(1);
            Cell cell_10 = row_1.createCell(0);
            cell_10.setCellValue("统计时间"); // (1,0)
            Cell cell_11 = row_1.createCell(1);
            cell_11.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss")); // (1,1)
    
            // 生成一个表 IO
            String Path = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/";
            FileOutputStream fileOutputStream = new FileOutputStream(Path + "观众统计表07.xlsx");
            workbook.write(fileOutputStream);
            fileOutputStream.close();
        }
    
        public static void test03() throws IOException {
            // 创建一个工作簿
            Workbook workbook = new HSSFWorkbook();
            // 创建一个工作表
            Sheet sheet = workbook.createSheet("sheet-1");
            // 创建一行
            Row row_0 = sheet.createRow(0); // 第一行
    
            // 创建一个单元格
            Cell cell_00 = row_0.createCell(0);
            cell_00.setCellValue("今日新增观众"); // (0,0)
            Cell cell_01 = row_0.createCell(1);
            cell_01.setCellValue(666); // (0,1)
    
            Row row_1 = sheet.createRow(1);
            Cell cell_10 = row_1.createCell(0);
            cell_10.setCellValue("统计时间"); // (1,0)
            Cell cell_11 = row_1.createCell(1);
            cell_11.setCellValue(new DateTime().toString("yyyy-MM-dd HH:mm:ss")); // (1,1)
    
            // 生成一个表 IO
            String Path = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/";
            FileOutputStream fileOutputStream = new FileOutputStream(Path + "观众统计表03.xls");
            workbook.write(fileOutputStream);
            fileOutputStream.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    2 数据批量导入

    大文件写HSSF,缺点:最多只能处理65536行,否则会抛出异常。

    java. lang. IlegalArgumentException: Invalid row number (65536) outside al lowable range (0. .65535)
    
    • 1

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

    大文件写XSSF,缺点:写数据时速度非常慢,非常耗内存,也会发生内存溢出,如100万条。
    优点:可以写较大的数据量,如20万条。

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

    注意:过程中会产生临时文件,需要清理临时文件。

    默认由100条记录被保存在内存中,如果超过这数量,则最前面的数据被写入临时文件。如果想自定义内存中数据的数量,可以使用new SXSSFWorkbook (数量)。

    SXSSFWorkbook-来至官方的解释:实现BigGridDemo策略的流式XSSFWorkbook版本。这允许写入非常大的文件而不会耗尽內存,因为任何时候只有可配置的行部分被保存在内存中。请注意,仍然可能会消耗大量內存,这些内存基于您正在使用的功能,例如合并区域,注释……仍然只存储在内存中,因此如果广泛使用,可能需要大量内存。

    1 HSSFWorkbook - 2.242s

     public static void testHSSFWorkbookBD() throws IOException {
            // 时间
            long startTime = System.currentTimeMillis();
            Workbook workbook = new HSSFWorkbook();
            Sheet sheet = workbook.createSheet();
            /**
             * Exception in thread "main" java.lang.IllegalArgumentException: Invalid row number (65536) outside allowable range (0..65535)
             */
            for (int i = 0; i < 65536; i++) {
                Row row = sheet.createRow(i);
                for (int j = 0; j < 10; j++) {
                    Cell cell = row.createCell(j);
                    cell.setCellValue(j);
                }
            }
            System.out.println("over");
            String Path = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/";
            FileOutputStream fileOutputStream = new FileOutputStream(Path + "HSSFWorkbook03.xls");
            workbook.write(fileOutputStream);
            fileOutputStream.close();
            long endTime = System.currentTimeMillis();
            System.out.println((double) (endTime - startTime) / 1000); // 时间比较快 数据量有限
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2 XSSFWorkbook - 13.682s

     public static void testXSSFWorkbookBD() throws IOException {
            // 时间
            long startTime = System.currentTimeMillis();
            Workbook workbook = new XSSFWorkbook();
            Sheet sheet = workbook.createSheet();
            for (int i = 0; i < 200000; i++) {
                Row row = sheet.createRow(i);
                for (int j = 0; j < 10; j++) {
                    Cell cell = row.createCell(j);
                    cell.setCellValue(j);
                }
            }
            System.out.println("over");
            String Path = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/";
            FileOutputStream fileOutputStream = new FileOutputStream(Path + "HSSFWorkbook07.xlsx");
            workbook.write(fileOutputStream);
            fileOutputStream.close();
            long endTime = System.currentTimeMillis();
            System.out.println((double) (endTime - startTime) / 1000); // 5.064 时间长 但是可以写入更加多的数据
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3 SXSSFWorkbook - 5.788s

     public static void testBDSXSSFWorkbook() throws IOException {
            // 时间
            long startTime = System.currentTimeMillis();
            SXSSFWorkbook workbook = new SXSSFWorkbook();
            Sheet sheet = workbook.createSheet();
            for (int i = 0; i < 200000; i++) {
                Row row = sheet.createRow(i);
                for (int j = 0; j < 10; j++) {
                    Cell cell = row.createCell(j);
                    cell.setCellValue(j);
                }
            }
            System.out.println("over");
            String Path = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/";
            FileOutputStream fileOutputStream = new FileOutputStream(Path + "SXSSFWorkbook.xlsx");
            workbook.write(fileOutputStream);
            // 清除临时文件
            workbook.dispose();
            fileOutputStream.close();
            long endTime = System.currentTimeMillis();
            System.out.println((double) (endTime - startTime) / 1000);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3 POI读

    1 基础版本- 注意获取值的类型

    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 java.io.FileInputStream;
    import java.io.IOException;
    
    public class POIRead {
        public static void main(String[] args) throws IOException {
            test07();
        }
    
        public static void test03() throws IOException {
            // 获取文件流
            String PATH = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/观众统计表03.xls";
            FileInputStream fileInputStream = new FileInputStream(PATH);
            // 创建一个工作簿
            Workbook workbook = new HSSFWorkbook(fileInputStream);
            Sheet sheet = workbook.getSheetAt(0);
            Row row = sheet.getRow(0);
            Cell cell = row.getCell(1);
            /**
             * 获取不同的类型
             * System.out.println(cell.getStringCellValue());
             */
            System.out.println(cell.getNumericCellValue());
            fileInputStream.close();
        }
    
        public static void test07() throws IOException {
            // 获取文件流
            String PATH = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/观众统计表07.xlsx";
            FileInputStream fileInputStream = new FileInputStream(PATH);
            // 创建一个工作簿
            Workbook workbook = new XSSFWorkbook(fileInputStream);
            Sheet sheet = workbook.getSheetAt(0);
            Row row = sheet.getRow(0);
            Cell cell = row.getCell(1);
            /**
             * 获取不同的类型
             * System.out.println(cell.getStringCellValue());
             */
            System.out.println(cell.getNumericCellValue());
            fileInputStream.close();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51

    2 读取不同类型的值

    package com.poi;
    public class POIRead {
        public static void main(String[] args) throws IOException {
            testCellType();
        }
    
        public static void testCellType() throws IOException {
            // 获取文件流
            String PATH = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/住址.xlsx";
            FileInputStream fileInputStream = new FileInputStream(PATH);
            // 创建一个工作簿
            Workbook workbook = new XSSFWorkbook(fileInputStream);
            Sheet sheet = workbook.getSheetAt(0);
            Row rowTitle = sheet.getRow(0);
            if (null != rowTitle) {
                int cells = rowTitle.getPhysicalNumberOfCells();
                for (int i = 0; i < cells; i++) {
                    Cell cell = rowTitle.getCell(i);
                    if (null != cell) {
                        CellType cellType = cell.getCellType();
                        String cellValue = cell.getStringCellValue();
                        System.out.print(cellValue + " | ");
                    }
                }
            }
            System.out.println();
            int rows = sheet.getPhysicalNumberOfRows();
            for (int i = 1; i < rows; i++) {
                Row rowData = sheet.getRow(i);
                if (null != rowData) {
                    int cells = rowData.getPhysicalNumberOfCells();
                    for (int j = 0; j < cells; j++) {
                        Cell cell = rowData.getCell(j);
                        if (null != cell) {
                            CellType cellType = cell.getCellType();
                            String cellValue = "";
                            // 匹配类型
                            switch (cellType) {
                                case STRING:
                                    cellValue = cell.getStringCellValue();
                                    break;
                                case BOOLEAN:
                                    cellValue = String.valueOf(cell.getBooleanCellValue());
                                    break;
                                case BLANK:
                                    break;
                                case NUMERIC:
                                    if (DateUtil.isCellDateFormatted(cell)) {
                                        cellValue = new DateTime(cell.getDateCellValue()).toString("yyyy-MM-dd");
                                    } else {
                                        cellValue = new DecimalFormat("0").format(cell.getNumericCellValue());
                                    }
                                    break;
                                case ERROR:
                                    break;
                            }
                            System.out.print(cellValue + " | ");
                        }
                    }
                    System.out.println();
                }
            }
    
            fileInputStream.close();
        }
    
    
        public static void test03() throws IOException {
            // 获取文件流
            String PATH = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/观众统计表03.xls";
            FileInputStream fileInputStream = new FileInputStream(PATH);
            // 创建一个工作簿
            Workbook workbook = new HSSFWorkbook(fileInputStream);
            Sheet sheet = workbook.getSheetAt(0);
            Row row = sheet.getRow(0);
            Cell cell = row.getCell(1);
            /**
             * 获取不同的类型
             * System.out.println(cell.getStringCellValue());
             */
            System.out.println(cell.getNumericCellValue());
            fileInputStream.close();
        }
    
        public static void test07() throws IOException {
            // 获取文件流
            String PATH = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/观众统计表07.xlsx";
            FileInputStream fileInputStream = new FileInputStream(PATH);
            // 创建一个工作簿
            Workbook workbook = new XSSFWorkbook(fileInputStream);
            Sheet sheet = workbook.getSheetAt(0);
            Row row = sheet.getRow(0);
            Cell cell = row.getCell(1);
            /**
             * 获取不同的类型
             * System.out.println(cell.getStringCellValue());
             */
            System.out.println(cell.getNumericCellValue());
            fileInputStream.close();
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103

    4 easyEXCEL

    <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>3.0.5</version>
    </dependency>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    1 通用数据生成

        private static List<DemoData> data() {
            List<DemoData> list = ListUtils.newArrayList();
            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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    2 最简单的写

    在这里插入图片描述

    2.1 最简单的写的对象

    package com.easyexcel;
    @Data
    @EqualsAndHashCode
    public class DemoData {
        @ExcelProperty("字符串标题")
        private String string;
        @ExcelProperty("日期标题")
        private Date date;
        @ExcelProperty("数字标题")
        private Double doubleData;
        /**
         * 忽略这个字段
         */
        @ExcelIgnore
        private String ignore;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.2 代码

    /**
         * 最简单的写
         * 

    * 1. 创建excel对应的实体对象 参照{@link DemoData} *

    * 2. 直接写即可 */ @Test public void simpleWrite() { // 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入 String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel.xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // 如果这里想使用03 则 传入excelType参数即可 // 分页查询数据 EasyExcel.write(fileName, DemoData.class) .sheet("模板") .doWrite(TestEasyExcel::data); }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    3 根据参数只导出指定列

    /**
         * 根据参数只导出指定列
         * 

    * 1. 创建excel对应的实体对象 参照{@link DemoData} *

    * 2. 根据自己或者排除自己需要的列 *

    * 3. 直接写即可 * * @since 2.1.1 */ @Test public void excludeOrIncludeWrite() { String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel-1.xlsx"; // 这里需要注意 在使用ExcelProperty注解的使用,如果想不空列则需要加入order字段,而不是index,order会忽略空列,然后继续往后,而index,不会忽略空列,在第几列就是第几列。 // 根据用户传入字段 假设我们要忽略 date Set<String> excludeColumnFiledNames = new HashSet<String>(); excludeColumnFiledNames.add("date"); // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, DemoData.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("模板") .doWrite(data()); String fileName_2 = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel-2.xlsx"; // 根据用户传入字段 假设我们只要导出 date Set<String> includeColumnFiledNames = new HashSet<String>(); includeColumnFiledNames.add("date"); // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName_2, DemoData.class).includeColumnFiledNames(includeColumnFiledNames).sheet("模板") .doWrite(data()); }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    4 指定写入的列

    package com.easyexcel;
    @Getter
    @Setter
    @EqualsAndHashCode
    public class IndexData {
        @ExcelProperty(value = "字符串标题", index = 0)
        private String string;
        @ExcelProperty(value = "日期标题", index = 1)
        private Date date;
        /**
         * 这里设置3 会导致第二列空的
         */
        @ExcelProperty(value = "数字标题", index = 3)
        private Double doubleData;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
        /**
         * 指定写入的列
         * 

    1. 创建excel对应的实体对象 参照{@link IndexData} *

    2. 使用{@link ExcelProperty}注解指定写入的列 *

    3. 直接写即可 */ @Test public void indexWrite() { String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel-3.xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, IndexData.class).sheet("模板").doWrite(data()); }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    在这里插入图片描述

    5 复杂头写入

    在这里插入图片描述

    package com.easyexcel;
    @Getter
    @Setter
    @EqualsAndHashCode
    public class ComplexHeadData {
        @ExcelProperty({"主标题", "字符串标题"})
        private String string;
        @ExcelProperty({"主标题", "日期标题"})
        private Date date;
        @ExcelProperty({"主标题", "数字标题"})
        private Double doubleData;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
        /**
         * 复杂头写入
         * 

    1. 创建excel对应的实体对象 参照{@link ComplexHeadData} *

    2. 使用{@link ExcelProperty}注解指定复杂的头 *

    3. 直接写即可 */ @Test public void complexHeadWrite() { String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel-4.xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, ComplexHeadData.class).sheet("模板").doWrite(data()); }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    6 重复多次写入(写到单个或者多个Sheet)

    /**
         * 重复多次写入
         * 直接调用二次写入即可
         */
        @Test
        public void repeatedWrite() {
            // 方法1: 如果写到同一个sheet
            String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel.xlsx";
            // 这里 需要指定写用哪个class去写
            ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
            // 这里注意 如果同一个sheet只要创建一次
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来
            for (int i = 0; i < 5; i++) {
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                List<DemoData> data = data();
                excelWriter.write(data, writeSheet);
            }
            excelWriter.finish();
        }
            // 方法2: 如果写到不同的sheet 同一个对象
            ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
            // 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面
            for (int i = 0; i < 5; i++) {
                // 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样
                WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();
                // 分页去数据库查询数据 这里可以去数据库查询每一页的数据
                List<DemoData> data = data();
                excelWriter.write(data, writeSheet);
            }
            excelWriter.finish();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    7 日期、数字或者自定义格式转换

    在这里插入图片描述

    package com.easyexcel;
    
    import com.alibaba.excel.annotation.ExcelProperty;
    import com.alibaba.excel.annotation.format.DateTimeFormat;
    import com.alibaba.excel.annotation.format.NumberFormat;
    import lombok.EqualsAndHashCode;
    import lombok.Getter;
    import lombok.Setter;
    
    import java.util.Date;
    
    @Getter
    @Setter
    @EqualsAndHashCode
    public class ConverterData {
        /**
         * 我想所有的 字符串起前面加上"自定义:"三个字
         */
        @ExcelProperty(value = "字符串标题")
        private String string;
        /**
         * 我想写到excel 用年月日的格式
         */
        @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
        @ExcelProperty("日期标题")
        private Date date;
        /**
         * 我想写到excel 用百分比表示
         */
        @NumberFormat("#.##%")
        @ExcelProperty(value = "数字标题")
        private Double doubleData;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
        /**
         * 日期、数字或者自定义格式转换
         * 

    1. 创建excel对应的实体对象 参照{@link ConverterData} *

    2. 使用{@link ExcelProperty}配合使用注解{@link DateTimeFormat}、{@link NumberFormat}或者自定义注解 *

    3. 直接写即可 */ @Test public void converterWrite() { String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel.xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 EasyExcel.write(fileName, ConverterData.class).sheet("模板").doWrite(data()); }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    8 最简单的读

    1 最简单的读的监听器

    package com.easyexcel;
    // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
    @Slf4j
    public class DemoDataListener implements ReadListener<DemoData> {
    
        /**
         * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
         */
        private static final int BATCH_COUNT = 100;
        /**
         * 缓存的数据
         */
        private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        /**
         * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
         */
        private DemoDAO demoDAO;
    
        public DemoDataListener() {
            // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
            demoDAO = new DemoDAO();
        }
    
        /**
         * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
         *
         * @param demoDAO
         */
        public DemoDataListener(DemoDAO demoDAO) {
            this.demoDAO = demoDAO;
        }
    
        /**
         * 这个每一条数据解析都会来调用
         *
         * @param data    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
         * @param context
         */
        @Override
        public void invoke(DemoData data, AnalysisContext context) {
            log.info("解析到一条数据:{}", JSON.toJSONString(data));
            cachedDataList.add(data);
            // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
            if (cachedDataList.size() >= BATCH_COUNT) {
                saveData();
                // 存储完成清理 list
                cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
            }
        }
    
        /**
         * 所有数据解析完成了 都会来调用
         *
         * @param context
         */
        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            // 这里也要保存数据,确保最后遗留的数据也存储到数据库
            saveData();
            log.info("所有数据解析完成!");
        }
    
        /**
         * 加上存储数据库
         */
        private void saveData() {
            log.info("{}条数据,开始存储数据库!", cachedDataList.size());
            demoDAO.save(cachedDataList);
            log.info("存储数据库成功!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71

    2 持久层

    package com.easyexcel;
    /**
     * 假设这个是你的DAO存储。当然还要这个类让spring管理,当然你不用需要存储,也不需要这个类。
     **/
    public class DemoDAO {
        public void save(List<DemoData> list) {
            // 持久化操作
            // 如果是mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3 代码

     /**
         * 最简单的读
         * 

    * 1. 创建excel对应的实体对象 参照{@link DemoData} *

    * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener} *

    * 3. 直接读即可 */ @Test public void simpleRead() { // 写法1:JDK8+ ,不用额外写一个DemoDataListener // since: 3.0.0-beta1 String fileName = "/Users/zhaoshuai11/Desktop/utools/src/main/resources/file/easyExcel.xlsx"; // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭 // 这里每次会读取100条数据 然后返回过来 直接调用使用数据就行 EasyExcel.read(fileName, DemoData.class, new PageReadListener<DemoData>(dataList -> { for (DemoData demoData : dataList) { log.info("读取到一条数据{}", JSON.toJSONString(demoData)); } })).sheet().doRead(); }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    在这里插入图片描述

    // 写法2:
            // 匿名内部类 不用额外写一个DemoDataListener
            // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
            read(fileName, DemoData.class, new ReadListener<DemoData>() {
                /**
                 * 单次缓存的数据量
                 */
                public static final int BATCH_COUNT = 100;
                /**
                 *临时存储
                 */
                private List<DemoData> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    
                @Override
                public void invoke(DemoData data, AnalysisContext context) {
                    cachedDataList.add(data);
                    if (cachedDataList.size() >= BATCH_COUNT) {
                        saveData();
                        // 存储完成清理 list
                        cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                    }
                }
    
                @Override
                public void doAfterAllAnalysed(AnalysisContext context) {
                    saveData();
                }
    
                /**
                 * 加上存储数据库
                 */
                private void saveData() {
                    log.info("{}条数据,开始存储数据库!", cachedDataList.size());
                    log.info("存储数据库成功!");
                }
            }).sheet().doRead();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    在这里插入图片描述

            // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
            // 写法3:
            // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
            EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

            // 写法4
            // 一个文件一个reader
            ExcelReader excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
            // 构建一个sheet 这里可以指定名字或者no
            ReadSheet readSheet = EasyExcel.readSheet(0).build();
            // 读取一个sheet
            excelReader.read(readSheet);
            excelReader.finish();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
  • 相关阅读:
    PHP GET,POST请求file_get_contents拼接header
    Python+人工智能基础大纲
    Android驱动框架整理之KERNEL框架
    springboot物业公司收费车位报修管理系统tbr18-java-ssm
    浅谈风力发电场集中监控系统解决方案
    Android挂载系统分区执行mount和remount
    使用Python处理ADC激光测距数据并绘制为图片(二)
    【opencv】opencv开发包简介
    3-docker安装centos7
    Abnormal build process termination:错误解决
  • 原文地址:https://blog.csdn.net/zs18753479279/article/details/126206178