Excel导入导出详解

Lou.Chen2022年7月8日
大约 13 分钟

Excel导出重点代码详解

本位主要参考阿里开源EasyExcel

EasyExcel参考 https://www.yuque.com/easyexcel/doc/easyexcelopen in new window

POI参考:https://poi.apache.org/components/open in new window

一、技术简介

1.1 技术背景

本文主要为了解决一些常见应用问题的参考示例和痛点说明

例如,复杂表头的处理,表格的样式处理,字体的样式处理,动态头等的数据处理。

1.2 技术选型

Excel读写时候内存溢出

POI是选择Excel解析最多的框架,但这个框架并不那么完美。大部分使用POI都是使用他的userModel模式。userModel的好处是上手容易使用简单,随便拷贝个代码跑一下,剩下就是写业务转换了,虽然转换也要写上百行代码,相对比较好理解。然而userModel模式最大的问题是在于非常大的内存消耗,一个几兆的文件解析要用掉上百兆的内存。现在很多应用采用这种模式,之所以还正常在跑一定是并发不大,并发上来后一定会OOM或者频繁的full gc。

  • 这里选用EasyExcel能对存在大量数据时避免将全部全部数据一次加载到内存而是采用sax模式一行一行解析,并将一行的解析结果以观察者的模式通知处理。

二、项目搭建

2.1 快速开始

2.1.1 新建SpringBoot工程

2.1.2 引入核心依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.10</version>
</dependency>

2.2 读取Excel

2.2.1 基本读

  • 数据模型
@Data
public class DemoBase {
    private String string;
    private Date date;
    private Double doubleData;
}
  • 读取的监听器 AnalysisEventListener<T>
    • 读取的时候只需要继承AnalysisEventListener监听器后在 invoke方法中获取相应的数据行进行业务处理即可
@RequiredArgsConstructor

public class DemoExcelListener extends AnalysisEventListener<DemoData> {

    /**

     * 读取excel 一行一行读

     * 第一行作为标题不会被读取

     * @param demoData

     * @param analysisContext

     */

    public void invoke(DemoData demoData, AnalysisContext analysisContext) {

        //行数据

        System.out.println(demoData.toString());

        //其他业务操作

        //-----------------

    }



    /**

     * 读取完之后的执行操作

     * @param analysisContext

     */

    @Override

    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

        //保存到数据库等

    }

}
  • 控制器
@RestController

@RequestMapping("/excel")

public class DemoController {



    @PostMapping("/import")

    public String upload(MultipartFile file) throws Exception {

        try {

            InputStream inputStream = file.getInputStream();

            //需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭

            //EasyExcel.read(输入流,数据模型源,监听器)

            EasyExcel.read(inputStream, DemoBase.class,new DemoExcelListener()).sheet().doRead();

        } catch (IOException e) {

            throw new Exception("文件读取失败,请重试!");

        }

        return "导入成功";

    }

}

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

有时Excel读取的内容不想按照原来的格式输出,而是按照自定义的格式通过一定规则进行读取。

  • ①读Excel的性别字段值为 的,代表数据库字段为1,字段值为 的,代表数据库字段为0

  • ②按照 yyyy年MM月dd日HH时mm分ss秒 格式接收日期

  • ③接收#.##%百分比的数字

  • 数据模型

@Data

public class DemoBase {

    private String string;

    

    @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")

    private Date date;

    

    @NumberFormat("#.##%")

    private Double doubleData;

    

    @ExcelProperty(converter = CustomSexStringConverter.class)

    private Integer gender;

}
  • 转换器

    Converter<T>

    • 自定义转换器实现Converter<T>接口
    • supportJavaTypeKey返回支持Java字段类型
    • supportExcelTypeKey返回支持的表格读取类型
    • convertToJavaData 读取时,数据转换具体实现
public class CustomSexStringConverter implements Converter<Integer> {

    @Override

    public Class supportJavaTypeKey() {

        return Integer.class;

    }



    @Override

    public CellDataTypeEnum supportExcelTypeKey() {

        return CellDataTypeEnum.STRING;

    }



    //读取的excel内容转换

    @Override

    public Integer convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {

        return "男".equals(cellData.getStringValue()) ? 1 : 0;

    }



    //这里为写入excel方法,现在不用管

    @Override

    public CellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {

        return new CellData(value.equals(1) ? "男" : "女");

    }

}
  • 监听器 参考2.2.1
  • 控制器
@RestController

@RequestMapping("/excel")

@RequiredArgsConstructor

public class DemoController {

    @CrossOrigin

    @PostMapping("/import")

    public ResponseModel<?> upload(MultipartFile file) throws Exception {

         try {

            InputStream inputStream = file.getInputStream();

            //需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭

            //EasyExcel.read(输入流,数据模型源,监听器)

            EasyExcel.read(inputStream, DemoBase.class,new DemoExcelListener(this)).sheet().doRead();

         } catch (IOException e) {

            throw new Exception("文件读取失败,请重试!");

        }

            return ResponseModel.ok();

    }

}

2.2.3 指定列的下标或者列名

@Data

public class IndexOrNameData {

    /**

     * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配

     */

    @ExcelProperty(index = 2)

    private Double doubleData;

    /**

     * 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据

     */

    @ExcelProperty("字符串标题")

    private String string;

    @ExcelProperty("日期标题")

    private Date date;

}

2.2.4 多行头

headRowNumber这里可以设置2,Excel这里的头设置为两行。不传入也可以,因为默认会根据DemoBase来解析,他没有指定头,也就是默认1

@RestController

@RequestMapping("/excel")

public class DemoController {



    @PostMapping("/import")

    public String upload(MultipartFile file) throws Exception {

        try {

            InputStream inputStream = file.getInputStream();

            //需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭

            //EasyExcel.read(输入流,数据模型源,监听器)

            EasyExcel.read(inputStream, DemoBase.class,new DemoExcelListener()).sheet().headRowNumber(2).doRead();

        } catch (IOException e) {

            throw new Exception("文件读取失败,请重试!");

        }

        return "导入成功";

    }

}

2.2.5 数据异常转换处理

  • 数据模型 参考2.2.2
  • 监听器
    • onException方法: 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
@Slf4j

public class DemoExcelListener extends AnalysisEventListener<DemoBase> {



    @Override

    public void invoke(DemoBase data, AnalysisContext context) {

        //行数据

        System.out.println(data.toString());

        //其他业务操作

    }



    /**

     * 读取完后的操作

     * @param context

     */

    @Override

    public void doAfterAllAnalysed(AnalysisContext context) {

        //保存数据库等

    }



    /**

     * 在转换异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。

     * @param exception

     * @param context

     * @throws Exception

     */

    @Override

    public void onException(Exception exception, AnalysisContext context) throws Exception {

        log.warn("解析失败,继续解析下一行数据。异常信息:{}",exception.getMessage());

        if (exception instanceof ExcelDataConvertException) {

            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;

            log.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),

                    excelDataConvertException.getColumnIndex());

        }

    }

}
  • 控制器 参考2.2.2

2.3 写入Excel

2.3.1 基本写

  • 数据模型
@Data

public class DemoWrite{

    @ExcelProperty("字符串标题")

    private String string;

    @ExcelProperty("日期标题")

    private Date date;

    @ExcelProperty("数字标题")

    private Double doubleData;

    /**

     * 忽略这个字段

     */

    @ExcelIgnore

    private String ignore;

}
  • 控制器
@RestController

@RequestMapping("/excel")

public class DemoController {

    @GetMapping("/export")

    public void export(HttpServletResponse response) throws IOException {

        // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman

        response.setContentType("application/vnd.ms-excel");

        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(), DemoWrite.class).sheet("模板").doWrite(data());

    }

    

    //初始化数据

    private List<DemoWrite> data() {

    List<DemoWrite> list = new ArrayList<DemoWrite>();

    for (int i = 0; i < 10; i++) {

        DemoWrite data = new DemoWrite();

        data.setString("字符串" + i);

        data.setDate(new Date());

        data.setDoubleData(0.56);

        list.add(data);

    }

    return list;

    }

}

2.3.2 指定写入的列

  • 数据模型
@Data

public class DemoWrite {

    @ExcelProperty(value = "字符串标题",index = 0)

    private String string;

    @ExcelProperty(value = "日期标题",index = 1)

    private Date date;

    /**

     * 这里设置3 会导致第二列空的

     */

    @ExcelProperty(value = "数字标题",index = 3)

    private Double doubleData;

    /**

     * 忽略这个字段

     */

    @ExcelIgnore

    private String ignore;

}
  • 控制器 参考2.3.1

2.3.3 复杂头的写入

  • 数据模型
@Data

public class DemoWrite {

    @ExcelProperty(value = {"主标题","字符串标题"})

    private String string;

    @ExcelProperty(value = {"主标题","日期标题"})

    private Date date;

    @ExcelProperty(value = {"主标题","数字标题"})

    private Double doubleData;

    /**

     * 忽略这个字段

     */

    @ExcelIgnore

    private String ignore;

}
  • 控制器 参考2.3.1

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

有时Excel写入的内容不想按照原来的格式写入,而是按照自定义的格式通过一定规则进行写入。

  • ①读取数据库的性别字段值为 0 的,代表实际含义为,读取数据库字段值为 1的,代表实际含义为

  • ②按照 yyyy年MM月dd日HH时mm分ss秒 格式写入日期

  • ③写入#.##%百分比的数字

  • 数据模型

@Data

public class DemoBase {



    @ExcelProperty(value = "字符串标题")

    private String string;



    @ExcelProperty("日期标题")

    @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")

    private Date date;



    @ExcelProperty(value = "数字标题")

    @NumberFormat("#.##%")

    private Double doubleData;



    @ExcelProperty(value = "性别",converter = CustomSexStringConverter.class)

    private Integer gender;

}
  • 转换器

    Converter<T>

    • 自定义转换器实现Converter<T>接口
    • supportJavaTypeKey返回支持Java字段类型
    • supportExcelTypeKey返回支持的表格读取类型
    • convertToExcelData 写入时,数据转换具体实现
public class CustomSexStringConverter implements Converter<Integer> {

    @Override

    public Class supportJavaTypeKey() {

        return Integer.class;

    }



    @Override

    public CellDataTypeEnum supportExcelTypeKey() {

        return CellDataTypeEnum.STRING;

    }



    //这里为读取的处理方法,不用管

    @Override

    public Integer convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {

        return "男".equals(cellData.getStringValue()) ? 1 : 0;

    }



    @Override

    public CellData convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {

        return new CellData(value.equals(1) ? "男" : "女");

    }

}
  • 控制器
@RestController

@RequestMapping("/excel")

public class DemoController {

    @GetMapping("/export")

    public void export(HttpServletResponse response) throws IOException {

        // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman

        response.setContentType("application/vnd.ms-excel");

        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(), DemoBase.class).sheet("模板").doWrite(dataInit());

    }

    //初始化数据

    private List<DemoBase> dataInit() {

        List<DemoBase> list=new ArrayList<>();

        for (int i = 0; i < 10; i++) {

            DemoBase data = new DemoBase();

            data.setString("字符串" + i);

            data.setDate(new Date());

            data.setDoubleData(0.56);

            data.setGender(i%2);

            list.add(data);

        }

        return list;

   }

}

2.3.5 指定列宽列高

  • ContentRowHeight 正文内容的表格高度
  • HeadRowHeight 头的表格高度
  • 在类上ColumnWidth表示头的表格宽度
  • 在属性上ColumnWidth表示正文内容的表格宽度
@Data

@ContentRowHeight(10)

@HeadRowHeight(20)

@ColumnWidth(25)

public class WidthAndHeightData {

    @ExcelProperty("字符串标题")

    private String string;

    @ExcelProperty("日期标题")

    private Date date;

    /**

     * 宽度为50

     */

    @ColumnWidth(50)

    @ExcelProperty("数字标题")

    private Double doubleData;

}

2.3.6 给指定单元格添加下拉选项

  • 数据模型
@Data

public class DemoBase {



    @ExcelProperty(value = "字符串标题")

    private String string;



    @ExcelProperty("日期标题")

    @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")

    private Date date;



    @ExcelProperty(value = "数字标题")

    @NumberFormat("#.##%")

    private Double doubleData;



    @ExcelProperty(value = "性别",converter = CustomSexStringConverter.class)

    private Integer gender;



    @ExcelProperty(value = "合格")

    private String isPass;

}
  • 转换器 参考2.3.4

  • 添加

    SheetWriteHandler
    

    拦截器

    • 只需要继承SheetWriteHandler接口实现afterSheetCreate方法即可

    • afterSheetCreate具体的实现方法:

    • CellRangeAddressList表示添加的单元格位置,有四个构造参数,分别代表

①行索引开始位置 ② 行索引结束位置 ③列索引开始位置 ④列索引结束位置

  • 这里代表从第2行开始到11行结束,第5列开始到第5列结束生成下拉框

public class MySheetWriteHandler implements SheetWriteHandler {

    @Override

    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

    }



    @Override

    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

        CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 10, 4, 4);

        DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();

        DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"是", "否"});

        DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);

        //默认把显示的值以下拉框显示d

        dataValidation.setSuppressDropDownArrow(false);

        writeSheetHolder.getSheet().addValidationData(dataValidation);

    }

}
  • 控制器
    • 这里只需要在写入之前在registerWriteHandler方法中注册自定义的拦截器即可
@GetMapping("/export")

public void export(HttpServletResponse response) throws IOException {

    // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman

    response.setContentType("application/vnd.ms-excel");

    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(), DemoBase.class).sheet("模板").registerWriteHandler(new MySheetWriteHandler()).doWrite(dataInit());

}

2.3.7 锁定指定单元格

  • 数据模型 参考2.3.6

  • 转换器 参考 2.3.4

  • 添加拦截器

    CellWriteHandler
    
    • 这里只需要实现CellWriteHandler 接口中的afterCellDispose方法即可

    • afterCellDispose代表数据写完到表格之后的操作

    • 先设置所有单元格锁定,并设置密码为111,然后获取当前行的索引是否不为0(0代表标题行),然后再判断列是否在实际的索引范围内,若是则设定单元格格式为非锁定。这里还添加一个额外的动态样式设定,当索引为3,即为第四列时,若值为则设置表格颜色为红色

public class MyCellWriteHandler implements CellWriteHandler {

    @Override

    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {

    }



    @Override

    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }



    @Override

    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }



    @Override

    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

        Workbook workbook = writeSheetHolder.getSheet().getWorkbook();

        Sheet sheetAt = workbook.getSheetAt(0);

        sheetAt.protectSheet("111");

        CellStyle cellStyle = workbook.createCellStyle();

        cellStyle.setLocked(true);

        //索引从0开始

        int columnIndex = cell.getColumnIndex();

        int rowIndex= cell.getRowIndex();

        if (rowIndex != 0) {

            if (columnIndex <= 4) {

                CellStyle noneLock = workbook.createCellStyle();

                noneLock.setLocked(false);

                if (columnIndex == 3) {

                    String stringCellValue = cell.getStringCellValue();

                    if (StringUtils.equals(stringCellValue, "男")) {

                        Font font = workbook.createFont();

                        font.setColor(IndexedColors.RED.index);

                        noneLock.setFont(font);

                    }else{

                        noneLock.setFont(null);

                    }

                }

                noneLock.setAlignment(HorizontalAlignment.CENTER);

                cell.setCellStyle(noneLock);

            }

        }

    }

}
  • 控制器
    • 只需在registerWriteHandler中注册新的MyCellWriteHandler自定义拦截器即可
@GetMapping("/export")

public void export(HttpServletResponse response) throws IOException {

    // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman

    response.setContentType("application/vnd.ms-excel");

    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(), DemoBase.class).sheet("模板").registerWriteHandler(new MySheetWriteHandler()).registerWriteHandler(new MyCellWriteHandler()).doWrite(dataInit());

}
  • 结果
    • 标题和正文内容外不可编辑,需要编辑密码

2.3.8 限制只能输入指定值并提示

  • 数据模型
@Data

public class DemoBase {



    @ExcelProperty(value = "字符串标题")

    private String string;



    @ExcelProperty("日期标题")

    @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")

    private Date date;



    @ExcelProperty(value = "数字标题")

    @NumberFormat("#.##%")

    private Double doubleData;



    @ExcelProperty(value = "性别",converter = CustomSexStringConverter.class)

    private Integer gender;



    @ExcelProperty(value = "合格")

    private String isPass;



    @ExcelProperty(value = "测试列")

    private String testCell;

}
  • 拦截器

    SheetWriteHandler
    
    • 这里对数据值只能输入【是】或者【否】的值,否则进行提示
public class MySheetWriteHandler implements SheetWriteHandler {

    @Override

    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

    }



    @Override

    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

        //这里对 第1行到第10行 第5列做限制

        CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 10, 4, 4);

        DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();

        //固定值

        DataValidationConstraint constraint = helper.createExplicitListConstraint(new String[] {"是", "否"});

        DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);

        //设置固定的值不可见,非下拉

        dataValidation.setSuppressDropDownArrow(false);

        //提示框的类型

        dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);

        //提示框内容

        dataValidation.createErrorBox("错误提示", "只能输入【是】或者【否】");

        //展示提示框

        dataValidation.setShowErrorBox(true);

        //用户输入时的焦点提示

        dataValidation.createPromptBox("温馨提示", "只能输入【是】或者【否】");

        //展示提示框

dataValidation.setShowPromptBox(true);

        writeSheetHolder.getSheet().addValidationData(dataValidation);

    }

}

2.3.9 限制输入数字范围

  • 拦截器

    SheetWriteHandler
    
    • 这里限制100-200的整数
public class MySheetWriteHandler implements SheetWriteHandler {

    @Override

    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

    }



    @Override

    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

        //这里对 第1行到第10行 第6列做限制

        CellRangeAddressList cellRangeAddressList = new CellRangeAddressList(1, 10, 5, 5);

        DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();

        DataValidationConstraint constraint = helper.createNumericConstraint(DVConstraint.ValidationType.INTEGER, DVConstraint.OperatorType.BETWEEN, "100", "200");

        DataValidation dataValidation = helper.createValidation(constraint, cellRangeAddressList);

        dataValidation.setSuppressDropDownArrow(false);

        dataValidation.setErrorStyle(DataValidation.ErrorStyle.STOP);

        dataValidation.createErrorBox("错误提示", "只能输入100-200之间的整数");

        dataValidation.setShowErrorBox(true);

        dataValidation.createPromptBox("温馨提示", "只能输入100-200之间的整数");

        dataValidation.setShowPromptBox(true);

        writeSheetHolder.getSheet().addValidationData(dataValidation);

    }

}

2.3.10 各种类型输入限制说明

经过追踪源码发现,在DataValidationHelper接口中已经定义了所有常用验证器,而其内部实现都是通过DVConstraint实现的

  • 在使用时,只需要创建不同的验证器即可
DataValidationHelper helper = writeSheetHolder.getSheet().getDataValidationHelper();

 DataValidationConstraint constraint = helper.createNumericConstraint(DVConstraint.ValidationType.INTEGER, DVConstraint.OperatorType.BETWEEN, "100", "200");

常用验证器说明:

  • createNumericConstraint 数字校检器

  • createTextLengthConstraint 文本长度校检器

  • createTimeConstraint 时间校检器

  • createCustomConstraint 自定义校检器

  • createExplicitListConstraint 固定值校检器

    • 这里createExplicitListConstraint() 方法可以传递包含整数、浮点、日期或文本值的字符串数组。

public interface DataValidationHelper {

   DataValidationConstraint createFormulaListConstraint(String listFormula);



   DataValidationConstraint createExplicitListConstraint(String[] listOfValues);



   DataValidationConstraint createNumericConstraint(int validationType,int operatorType, String formula1, String formula2);

   

   DataValidationConstraint createTextLengthConstraint(int operatorType, String formula1, String formula2);

   

   DataValidationConstraint createDecimalConstraint(int operatorType, String formula1, String formula2);

   

   DataValidationConstraint createIntegerConstraint(int operatorType, String formula1, String formula2);

   

   DataValidationConstraint createDateConstraint(int operatorType, String formula1, String formula2,String dateFormat);

   

   DataValidationConstraint createTimeConstraint(int operatorType, String formula1, String formula2);

   

   DataValidationConstraint createCustomConstraint(String formula);

   

   DataValidation createValidation(DataValidationConstraint constraint,CellRangeAddressList cellRangeAddressList);

}

2.3.11 三种拦截器执行属性参考

执行顺序如下:

① SheetWriteHandler中的**beforeSheetCreate->afterSheetCreate

  • 创建第一个工作表之前的操作--> 创建第一个工作表之后的操作

RowWriteHandler中的**beforeRowCreate->afterRowCreate

  • 创建第一行表格之前的操作-->创建第一行表格之后的操作

CellWriteHandler中的**beforeCellCreate->afterCellCreate->afterCellDispose

  • 创建第一行第一列单元格之前的操作--> 创建第一行第一列单元格之后的操作-->创建第一行第一列内容后的操作

RowWriteHandler中的**afterRowDispose

  • 创建第一行数据后的操作