Easy Excel summary


title: Summary of Easy Excel usage
date: 2022-10-14 17:33:57
tags:

  • Excel
    categories:
  • Development technology and framework
    cover: https://cover.png
    feature: false

1 Overview

Official website address: EasyExcel official document - Java-based Excel processing tool | Easy Excel (alibaba.com)

EasyExcel is a Java-based, fast, concise Excel processing tool that solves memory overflow of large files. It allows you to quickly complete functions such as reading and writing Excel without considering factors such as performance and memory

insert image description here

import dependencies

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>最新版本</version>
</dependency>

2. Common annotations

2.2.1 @ExcelProperty

Used to match Excel and entity classes, the parameters are as follows:

name Defaults describe
value null It is used to match the header in Excel. It must match all of them. If there are multiple rows of headers, it will match the header of the last row
order Integer.MAX_VALUE The priority is higher than that value, and the order of the data in the entity and excel will be matched orderaccording the order of
index -1 The priority is higher than valueand order, and will be indexdirectly
converter automatically choose Specify what converter to use for the current field, and it will be automatically selected by default. In the case of reading, just implement com.alibaba.excel.converters.Converter#convertToJavaData(com.alibaba.excel.converters.ReadConverterContext<?>)the method

2.2.2 @ExcelIgnore

By default, all fields will match Excel, adding this annotation will ignore this field

2.2.3 @ExcelIgnoreUnannotated

By default, all fields with or ExcelPropertywithout annotations will participate in reading and writing ExcelIgnoreUnannotated. After annotations are added, fields without ExcelPropertyannotations will not participate

2.2.4 @DateTimeFormat

Date conversion, used Stringto receive data in Excel date format will call this annotation, the parameters are as follows:

name Defaults describe
value null java.text.SimpleDateFormatjust write
use1904windowing automatically choose The time in Excel is stored as a double-precision floating-point number starting from 1900, but sometimes the default start date is 1904, so set this value to start from 1904 by default

2.2.5 @NumberFormat

Digital conversion, used Stringto receive data in Excel digital format will call this annotation

name Defaults describe
value null java.text.DecimalFormatjust write
roundingMode RoundingMode.HALF_UP Set the rounding mode when formatting

3. Write Excel (export)

3.1 Common parameters

  • WriteWorkbook: Can be understood as an Excel
  • WriteSheet: understood as a form in Excel
  • WriteTable: If there are multiple actual tables in a form, you can useWriteTable

3.1.1 General parameters

WriteWorkbook, WriteSheet, WriteTablethere will be parameters, if it is empty, the default is to use the superior

name Defaults describe
converter null Many converters are loaded by default, and unsupported fields can be added here
writeHandler null written processor. It can be realized WorkbookWriteHandler, SheetWriteHandler, RowWriteHandler, CellWriteHandler, will be called at different stages of writing to Excel
relativeHeadRowIndex 0 Write to Excel and leave a few lines above
head null and choose one of clazzthe two . Read the list corresponding to the file header, and match the data according to the list. It is recommended to use class
clazz null and choose one of headthe two . Annotations can also be used to read the class corresponding to the header of the file. If neither is specified, all data will be read
autoTrim true Automatic trimming of headers, read data, etc.
use1904windowing false The time in Excel is stored as a double-precision floating-point number starting from 1900, but sometimes the default start date is 1904, so set this value to start from 1904 by default
useScientificFormat false Whether to use scientific notation for larger values ​​when converting numbers to text
needHead true Is it necessary to write headers to Excel
useDefaultStyle true Whether to use the default style
automaticMergeHead true Automatically merge headers, the same fields in the header will try to match up, down, left, and right
excludeColumnIndexes null Need to exclude the data of index in the object
excludeColumnFieldNames null Data that needs to exclude fields in the object
includeColumnIndexes null Just export the data of index in the object
includeColumnFieldNames null Just export the data of the fields in the object

3.1.2 WriteWorkbook

 EasyExcel.write(fileName, DemoData.class)
            // 在 write 方法之后, 在 sheet 方法之前都是设置 WriteWorkbook 的参数
            .sheet("模板")
            .doWrite(() -> {
    
    
                // 分页查询数据
                return data();
            });
name Defaults describe
excelType null The current type of Excel, support XLS, XLSX, CSV
outputStream null and choose one of filethe two . stream to write to the file
file null and choose one of outputStreamthe two . written file
templateInputStream null template file stream
templateFile null template file
charset Charset#defaultCharset Only csv files are useful, the encoding used when writing to the file
autoCloseStream true Automatically close the stream for writing
password null password to read the file
inMemory false Whether to process in memory, a temporary file will be generated by default to save memory. The memory mode efficiency will be better, but it is easy to OOM
writeExcelOnException false An exception was thrown during the writing process, whether to try to write the data to Excel

3.1.3 WriteSheet

 EasyExcel.write(fileName, DemoData.class)
            .sheet("模板")
             // 在 sheet 方法之后,在 doWrite 方法之前都是设置 WriteSheet 的参数
            .doWrite(() -> {
    
    
                // 分页查询数据
                return data();
            });
名称 默认值 描述
sheetNo 0 需要写入的编码
sheetName 需要些的 Sheet 名称,默认同 sheetNo

3.1.4 WriteTable

        EasyExcel.write(fileName, DemoData.class)
            .sheet("模板")
            .table()
            // 在 table 方法之后, 在 doWrite 方法之前都是设置 WriteTable 的参数
            .doWrite(() -> {
    
    
                // 分页查询数据
                return data();
            });
名称 默认值 描述
tableNo 0 需要写入的编码

3.2 简单的写

省略其他获取数据和定义接口等实现

insert image description here

3.2.1 定义表格实体类

public class ExcelDO {
    
    

    @ExcelProperty("员工ID")
    private String empId;
    @ExcelProperty("员工姓名")
    private String empName;
    @ExcelProperty("员工工号")
    private String empCode;
    @ExcelProperty("身份证号")
    private String idcardNo;
    @ExcelProperty("性别")
    private String gender;
    @ExcelProperty("电话号码")
    private String phone;
    @ExcelProperty("地址")
    private String address;
    @ExcelProperty("邮箱")
    private String email;
    @ExcelProperty("备注")
    private String remark;
    @ExcelProperty("有效标志")
    private String valiFlag;
    @ExcelProperty("创建时间")
    private Timestamp createTime;
    @ExcelIgnore
    private Timestamp updateTime;
}

3.2.2 实现类

@Service
public class ExcelServiceImpl implements ExcelService {
    
    

    @Resource
    private EmployeeService employeeService;

    @Override
    public void simpleWrite() {
    
    
        String fileName = "D://simpleWrite" + System.currentTimeMillis() + ".xlsx";

        // 写法1
        EasyExcel.write(fileName, ExcelDO.class)
                .sheet("模板")
                .doWrite(() -> getData());
  
        // 写法2
        EasyExcel.write(fileName, ExcelDO.class).sheet("模板").doWrite(getData());
  
        // 写法3
        try (ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelDO.class).build()) {
    
    
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            excelWriter.write(getData(), writeSheet);
        }
    }

    private List<ExcelDO> getData() {
    
    
        List<ExcelDO> list = ListUtils.newArrayList();

        List records = employeeService.getEmployee().getRecords();
        list.addAll(records);

        return list;
    }
}

3.2.3 Controller

@RestController
public class ExcelController {
    
    

    @Resource
    private ExcelService excelService;

    @PostMapping("/simpleWrite")
    public void simpleWrite() {
    
    
        excelService.simpleWrite();
    }
}

3.2.4 定义特殊格式转换 Convert

当使用 LocalDateTimeTimestamp 等格式时,会报 ExcelWriteDataConvertException,此时需要自定义类型转换器

insert image description here

TimestampConvert

public class TimestampConvert implements Converter<Timestamp> {
    
    

    // 在Java中数据类型
    @Override
    public Class<Timestamp> supportJavaTypeKey() {
    
    
        return Timestamp.class;
    }

    // 在Excel中的数据类型
    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
    
    
        return CellDataTypeEnum.STRING;
    }

    // 将Excel的数据类型转为Java数据类型
    @Override
    public Timestamp convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty,
                                       GlobalConfiguration globalConfiguration) throws Exception {
    
    
        return Timestamp.valueOf(cellData.getStringValue());
    }

    // 将Java的数据类型转为Excel数据类型
    @Override
    public WriteCellData<?> convertToExcelData(Timestamp value, ExcelContentProperty contentProperty,
                                               GlobalConfiguration globalConfiguration) throws Exception {
    
    
        return new WriteCellData<>(value.toString());
    }
}

3.2.5 Convert 使用

1、找到需要转换的字段,在 @ExcelProperty 上添加 converter 属性`

insert image description here

2、ExcelWriterBuilders 是支持对单次的操作添加 converter 的,那样就不需要为每个需要转换的字段单独添加 converter 了

insert image description here

3、全局 Convert

即将自定义的 Convert 添加到 EasyExcel 的 Convert Map 转换器 Map 里(Map<ConverterKey, Converter<?>> converterMap()

详细可见:EasyExcel 自定义 Converter 全局加载器以及加载 Converter

4、第二种方法还能这样写,在单次操作中加入转换器 Map 里

public void simpleWrite() {
    
    
        String fileName = "D://simpleWrite" + System.currentTimeMillis() + ".xlsx";

//        EasyExcel.write(fileName, ExcelDO.class)
//                .registerConverter(new TimestampConvert())
//                .sheet("模板")
//                .doWrite(() -> getData());
        ExcelWriter excelWriter = EasyExcel.write(fileName, ExcelDO.class).build();

        TimestampConvert timestampConvert = new TimestampConvert();
        excelWriter.writeContext().currentWriteHolder().converterMap()
                .put(ConverterKeyBuild.buildKey(timestampConvert.supportJavaTypeKey()), timestampConvert);
        excelWriter.writeContext().currentWriteHolder().converterMap()
                .put(ConverterKeyBuild.buildKey(timestampConvert.supportJavaTypeKey(), timestampConvert.supportExcelTypeKey()), timestampConvert);

        WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
        excelWriter.write(getData(), writeSheet);
}

3.2.6 定义内容转换 Convert

导出后,发现性别和有效标志等字段,显示的是数据库中存储的数字,应该将其转换为对应的含义

insert image description here

GenderConverter

public class GenderConverter implements Converter<String> {
    
    

    @Override
    public Class<String> supportJavaTypeKey() {
    
    
        return String.class;
    }

    @Override
    public CellDataTypeEnum supportExcelTypeKey() {
    
    
        return CellDataTypeEnum.STRING;
    }

    @Override
    public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
    
    
        return "男".equals(cellData.getStringValue()) ? "1" : "0";
    }

    @Override
    public WriteCellData<?> convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
    
    
        return new WriteCellData<>(value.equals("1") ? "男" : "女");
    }
}

使用 Convert 的第一种使用方法

insert image description here

再导出后,性别已经由数字转换为对应的含义

insert image description here

3.3 Web 中的写(下载)

与简单的写类似

@RestController
public class ExcelController {
    
    

    @Resource
    private ExcelService excelService;

    @GetMapping("/download")
    public void download(HttpServletResponse response) throws IOException {
    
    
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("UTF-8");
        // 这里 URLEncoder.encode可以防止中文乱码,和 EasyExcel 没有关系
        String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        EasyExcel.write(response.getOutputStream(), ExcelDO.class).sheet("模板").doWrite(excelService.getData());
    }
}

浏览器地址栏输入接口地址,会弹出文件下载

insert image description here

insert image description here

下载失败的时候返回 JSON

@RestController
public class ExcelController {
    
    

    @Resource
    private ExcelService excelService;

    @GetMapping("/download")
    public void download(HttpServletResponse response) throws IOException {
    
    
        try {
    
    
            // 正确设置 response 的 content-type;即设置正确的 mime type
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("UTF-8");
            // 这里 URLEncoder.encode可以防止中文乱码 当然和 EasyExcel 没有关系
            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
  
//            int i = 1 / 0;
            EasyExcel.write(response.getOutputStream(), ExcelDO.class).sheet("模板").doWrite(excelService.getData());
        } catch (Exception e) {
    
    
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("UTF-8");
  
            response.getWriter().println(JSON.toJSONString(Result.fail("下载文件失败")));
        }
    }
}

insert image description here

4. 读 Excel(导入)

4.1 常用参数

  • ReadWorkbook 可以理解成一个 excel
  • ReadSheet 理解成一个 excel 里面的一个表单

4.1.1 通用参数

ReadWorkbook,ReadSheet 都会有的参数,如果为空,默认使用上级

名称 默认值 描述
converter 默认加载了很多转换器,这里可以加入不支持的字段
readListener 可以注册多个监听器,读取 Excel 的时候会不断的回调监听器中的方法
headRowNumber 1 excel 中头的行数,默认 1 行
head clazz 二选一。读取文件头对应的列表,会根据列表匹配数据,建议使用 class
clazz head 二选一。读取文件的头对应的 class,也可以使用注解。如果两个都不指定,则会读取全部数据
autoTrim true 会对头、读取数据等进行自动 trim
use1904windowing false Excel 中时间是存储 1900 年起的一个双精度浮点数,但是有时候默认开始日期是 1904,所以设置这个值改成默认 1904 年开始
useScientificFormat false 数字转文本的时候在较大的数值的是否是否采用科学计数法

4.1.2 ReadWorkbook

 EasyExcel.read(fileName, DemoData.class, new DemoDataListener())
           // 在 read 方法之后, 在 sheet方法之前都是设置ReadWorkbook的参数
           .sheet()
           .doRead();
名称 默认值 描述
excelType 当前 excel 的类型,支持 XLS、XLSX、CSV
inputStream file 二选一。读取文件的流,如果接收到的是流就只用,不用流建议使用 file 参数。因为使用了 inputStream easyexcel 会帮忙创建临时文件,最终还是 file
file inputStream 二选一。读取文件的文件。
mandatoryUseInputStream false 强制使用 inputStream 来创建对象,性能会变差,但是不会创建临文件。
charset Charset#defaultCharset 只有 csv 文件有用,读取文件的时候使用的编码
autoCloseStream true 自动关闭读取的流。
readCache 默认小于 5M 用 内存,超过 5M 会使用 EhCache,这里不建议使用这个参数。
readCacheSelector SimpleReadCacheSelector 用于选择什么时候用内存去存储临时数据,什么时候用磁盘存储临时数据
ignoreEmptyRow true 忽略空的行
password 读取文件的密码
xlsxSAXParserFactoryName 指定 sax 读取使用的 class 的名称,例如:com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
useDefaultListener true @since 2.1.4 默认会加入 ModelBuildEventListener 来帮忙转换成传入 class 的对象,设置成 false 后将不会协助转换对象,自定义的监听器会接收到 Map<Integer,CellData> 对象,如果还想继续接听到 class 对象,请调用 readListener 方法,加入自定义的 beforeListenerModelBuildEventListener、 自定义的 afterListener 即可。
extraReadSet 额外需要读取内容的 set,默认不读取这些数据

4.1.3 ReadSheet

 EasyExcel.read(fileName, DemoData.class, new DemoDataListener())
           .sheet()
           // 在 sheet 方法之后, 在 doRead方法之前都是设置ReadSheet的参数
           .doRead();
名称 默认值 描述
sheetNo 0 需要读取 Sheet 的编码,建议使用这个来指定读取哪个 Sheet
sheetName 根据名字去匹配 Sheet

4.2 简单的读

读取的表格就使用上面导出的表格

4.2.1 表格实体类和 Convert

表格实体类和 Convert 就使用写 Excel 的实体类和 Convert

4.2.2 实现类

@Service
public class ExcelServiceImpl implements ExcelService {
    
    

    @Resource
    private EmployeeDAO employeeDAO;

    public void simpleRead() {
    
    
        String fileName = "D:\\simpleWrite1665735316967.xlsx";

        // 写法1:不用额外写一个 ExcelListener
        // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
        // 这里每次会读取100条数据 然后返回过来 直接调用使用数据就行
        EasyExcel.read(fileName, ExcelDO.class, new PageReadListener<ExcelDO>(excelDOS -> {
    
    
            for (ExcelDO excelDO : excelDOS) {
    
    
                System.out.println(excelDO);
            }
        })).sheet().doRead();

        // 写法2:匿名内部类 不用额外写一个 ExcelListener
        EasyExcel.read(fileName, ExcelDO.class, new ReadListener<ExcelDO>() {
    
    
            // 单次缓存的数据量
            public static final int BATCH_COUNT = 100;
            // 临时存储
            private List<ExcelDO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

            @Override
            public void invoke(ExcelDO 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() {
    
    
                for (ExcelDO excelDO : cachedDataList) {
    
    
                    System.out.println(excelDO);
                }
            }
        }).sheet().doRead();

        // 写法3:有个很重要的点 ExcelListener 不能被Spring管理,要每次读取Excel都要new,然后里面用到Spring可以构造方法传进去
        EasyExcel.read(fileName, ExcelDO.class, new ExcelListener(employeeDAO)).sheet().doRead();

        // 写法4:一个文件一个reader
        try (ExcelReader excelReader = EasyExcel.read(fileName, ExcelDO.class, new ExcelListener(employeeDAO)).build()) {
    
    
            // 构建一个sheet 这里可以指定名字或者no
            ReadSheet readSheet = EasyExcel.readSheet(0).build();
            // 读取一个sheet
            excelReader.read(readSheet);
        }
    }
}

4.2.3 监听器

即把第二种写法的匿名内部类单独抽出来,监听器不能被 Spring 管理,每次读取 Excel 都要 new,然后里面用到 Spring 可以构造方法传进去

public class ExcelListener implements ReadListener<ExcelDO> {
    
    

    // 单次缓存的数据量
    public static final int BATCH_COUNT = 100;
    // 临时存储
    private List<ExcelDO> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    private EmployeeDAO employeeDAO;

    /**
     * 如果使用了Spring,使用这个构造方法。每次创建Listener的时候需要把Spring管理的类传进来
     *
     * @param employeeDAO
     */
    public ExcelListener(EmployeeDAO employeeDAO) {
    
    
        this.employeeDAO = employeeDAO;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data    one row value. Is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(ExcelDO data, AnalysisContext context) {
    
    
        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();
    }

    // 存储到数据库,批量存储
    private void saveData() {
    
    
//        employeeDAO.save(cachedDataList);
        for (ExcelDO excelDO : cachedDataList) {
    
    
            System.out.println(excelDO);
        }
    }
}

4.2.4 Controller

@RestController
public class ExcelController {
    
    

    @Resource
    private ExcelService excelService;

    @PostMapping("/simpleRead")
    public void simpleRead() {
    
    
        excelService.simpleRead();
    }
}

insert image description here

4.3 Web 中的读(上传)

与简单的读类似

@RestController
public class ExcelController {
    
    

    @Resource
    private EmployeeDAO employeeDAO;

    @PostMapping("upload")
    public Result upload(MultipartFile file) throws IOException {
    
    
        EasyExcel.read(file.getInputStream(), ExcelDO.class, new ExcelListener(employeeDAO)).sheet().doRead();
        return Result.success("上传成功");
    }
}

insert image description here

insert image description here

For more reading and writing related operations, please refer to the official document: About Easyexcel | Easy Excel (alibaba.com)

Guess you like

Origin blog.csdn.net/ACE_U_005A/article/details/127319923