EasyExcel的应用

ops/2025/1/18 4:11:21/

一、简单使用

        引入依赖:
        这里我们可以使用最新的4.0.2版本,也可以选择之前的稳定版本,3.1.x以后的版本API大致相同,新的版本也会向前兼容(3.1.x之前的版本,部分API可能在高版本被废弃),关于POI、JDK版本适配问题,具体可参考官网-版本说明。

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

        下载excel文件:

java">    @GetMapping("/download")public void excelDownload(HttpServletResponse response) throws IOException {response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");EasyExcel.write(response.getOutputStream(), Data.class).sheet("模板").doWrite(datas);String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");}

        读取excel文件:

java">    @PostMapping("/read")public void read(MultipartFile file) throws IOException {1、这只是简单演示,一般不使用 doReadSync 方法,此方法同步执行的,即它会阻塞当前线程,直到读取完整个Excel文件并返回所有数据。读取大型文件时,可能会导致程序响应变慢或阻塞。2、使用head映射字段时,该实体类上不能加 @Accessors 注解,加上此注解会字段映射不成功。3、一般会使用监听器 + doRead 方法实现excel文件的读取List<Data> datas = EasyExcel.read(file.getInputStream()).sheet().head(Data.class).doReadSync();System.out.println(datas);}

二、常用注解

        1、@ExcelProperty注解

                这个注解应该是最常用的注解,通常用来映射字段跟excel的列名,有以下几个属性:

名称默认值描述
value用于匹配excel中的头,必须全匹配,如果有多行头,会匹配最后一行头
orderInteger.MAX_VALUE优先级高于value,会根据order的顺序来匹配实体和excel中数据的顺序
index-1优先级高于valueorder,会根据index直接指定到excel中具体的哪一列
converter自动选择指定当前字段用什么转换器,默认会自动选择。写的情况下只要实现com.alibaba.excel.converters.Converter#convertToExcelData(com.alibaba.excel.converters.WriteConverterContext<T>) 方法即可

         注意: 

         1、如果没有特殊的调整一般,使用value属性就够了,在读取或者导出时都能匹配或者映射为对应的列名。
         2、value 跟 index 可以在导出数据的时候配合使用,value指定列名,index指定该列的顺序,例如:

    @ExcelProperty(value = "性别",index = 3) 代表列名为 性别,导出到第三列的位置。但是在导入时,如果设置了order属性,表示会根据指定列来匹配字段,例如上面就会将第三列匹配为性别字段,如果该列字段为空,或者字段类型不匹配就会报错,一般在读取数据时不会这么使用这个属性。

        3、order 属性代表按顺序匹配,比如说导出数据时,会按照字段上该属性的顺序,依次为列设置对应字段的值,比如order最小的,就匹配第一列的值,依次往后,在导出时也是一样,order最小的值,导出到第一列依次往后。

        4、converter:自定义的类型转换器,该属性可以实现自定义处理类,这个功能通常用来在 Excel 数据与 Java 对象之间进行特定格式的转换,例如日期、布尔值、自定义对象等。

  • 实现 Converter 接口
    要自定义一个转换器,需要实现 EasyExcel 提供的 Converter 接口。

  • 重写必要的方法

    • supportJavaTypeKey(): 指定支持的 Java 数据类型。
    • convertToExcelData(): 将 Java 数据类型转换为 Excel 单元格数据。
    • convertToJavaData(): 将 Excel 单元格数据转换为 Java 数据类型

        EasyExcel 自带了一些常用的转换器(例如 LocalDateConverterIntegerConverter 等),可以直接使用而无需自定义。

         例如:在姓名上加上该属性:

java">@ExcelProperty(value = "姓名",converter = ExcelStringConverter.class)
private String name;

        实现自定义处理类:

java">public class ExcelStringConverter implements Converter<String> {@Overridepublic Class<?> supportJavaTypeKey() {return String.class;}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return CellDataTypeEnum.STRING;}@Overridepublic String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {return cellData.getStringValue() + "导入数据进行处理!";}@Overridepublic WriteCellData<?> convertToExcelData(String value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {return new WriteCellData<>(value + "导出数据进行处理");}}

        例如实现在 Excel 中用 "是""否" 表示布尔值,而不是默认的 true/false

java">public class BooleanStringConverter implements Converter<Boolean> {@Overridepublic Class<?> supportJavaTypeKey() {return Boolean.class; // 支持的 Java 类型}@Overridepublic WriteCellData<?> convertToExcelData(Boolean value, ExcelContentProperty contentProperty) {return new WriteCellData<>(value ? "是" : "否"); // 将布尔值转为字符串}@Overridepublic Boolean convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty) {String stringValue = cellData.getStringValue();return "是".equals(stringValue); // 将字符串 "是"/"否" 转为布尔值}
}

         2、@ExcelIgnore注解
  • 作用范围:数据实体类的字段上;
  • 注解释义:当前字段不参与excel列的匹配,即处理时忽略该字段;

        在默认情况下,数据模型类中的所有字段都会参与匹配,可如果你定义的Java类中,有些字段在读写时并不需要参与进来,这时就可以给对应字段加上@ExcelIgnore注解,具备该注解的字段会被忽略。

        3、@ExcelIgnoreUnannotated注解 
  • 作用范围:数据模型类上;
  • 注解释义:匹配列时忽略所有未使用@ExcelProperty注解的字段;

        如果类中许多字段都不想参与excel读写,而你又嫌挨个加@ExcelIgnore注解麻烦,这时就可以直接在类上加一个@ExcelIgnoreUnannotated注解,以此来忽略所有未添加@ExcelProperty注解的字段。

        4、@DateTimeFormat注解
  • 作用范围:数据实体类的字段上;
  • 注解释义:用String接收日期数据时,会根据指定格式转换日期;
  • 可选参数:
    • value:日期数据转换为字符串的目标格式;
    • use1904windowing:excel日期数据默认从1900年开始,但有些会从1904开始;

        在解析excel文件时,如果使用String字段接收日期数据,就会根据指定的格式转换数据,格式可以参考java.text.SimpleDateFormat的写法,例如yyyy-MM-dd HH:mm:ss。而在往excel写数据时,如果Java中的字段类型为Date、LocalDate、LocalDateTime等日期类型,则会将日期数据转换为指定的格式写入对应列。

        例如:

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

        5、@NumberFormat注解     
  • 作用范围:数据实体类的字段上;
  • 注解释义:用String接收数值数据时,会根据指定格式转换数值;
  • 可选参数:
    • value:数值转换为字符串的目标格式;
    • roundingMode:数值格式化时的舍入模式,如四舍五入、向上取整等;

        这个注解和前一个注解类似,只不过是用于将非整数类型的数值数据转换成给定格式,格式可以参考java.text.DecimalFormat的写法,如#.##。除了可以指定格式外,还可以指定舍入模式,枚举可参考java.math.RoundingMode类。

        使用方法:

  • 指定数字格式
    使用 @NumberFormat 注解的 value 属性指定数字格式。例如:

    • #: 表示一个数字字符(整数部分)。
    • 0: 表示一个数字字符(小数部分,不足补零)。
    • ,: 表示千分位分隔符。
    • .: 表示小数点。
  • @ExcelProperty 搭配使用
    在数值类型字段上添加 @NumberFormat,并用 @ExcelProperty 指定列名。

java">    @ExcelProperty("销售金额")@NumberFormat("#,##0.00") // 指定数字格式,保留两位小数,带千分位private BigDecimal salesAmount;

       

三、常用生成注解

        1、@ColumnWidth注解
  • 作用范围:数据模型类上、字段上;
  • 注解释义:设置列的宽度;

        这个注解如果加在类上,则会对所有字段生效;如果单独加在某个字段上,则只对特定的列有效,单位是px

        例如:

java">    @ExcelProperty("销售金额")@ColumnWidth(200)private BigDecimal salesAmount;
        2、@ContentFontStyle注解
  • 作用范围:数据模型类上、字段上;
  • 注解释义:用于设置单元格内容字体格式的注解;
  • 可选参数:
    • fontName:字体名称,如“黑体、宋体、Arial”等;
    • fontHeightInPoints:字体高度,以磅为单位;
    • italic:是否设置斜体(字体倾斜);
    • strikeout:是否设置删除线;
    • color:字体的颜色,通过RGB值来设置;
    • typeOffset:偏移量,用于调整字体的位置;
    • underline:是否添加下划线;
    • bold:是否对字体加粗;
    • charset:设置编码格式,只能对全局生效(字段上设置无效)。

        这个注解用于设置主体内容的字体样式(不包含表头),与上个注解同理,加在类上对整个excel文件生效,加在字段上只对单列有效,可以通过该注解来设置字体风格、高度、是否斜体等属性。

java">    @ExcelProperty(value ="日期")@DateTimeFormat(value = "yyyy年MM月dd日 HH时mm分ss秒")@ContentFontStyle(fontName = "黑体",/* 字体类型 */fontHeightInPoints = 50, /* 字体高度,以磅为单位; */italic = BooleanEnum.TRUE,/* 是否设置斜体(字体倾斜); */strikeout = BooleanEnum.TRUE,/* 是否设置删除线; */color = 14,/* 字体的颜色,通过RGB值来设置;  0	黑色 (默认)9	红色10	绿色12	蓝色13	黄色14	粉色15	青色16	白色 */typeOffset = 1,/*偏移量,用于调整字体的位置; */underline = 1,/* 是否添加下划线; */bold = BooleanEnum.TRUE/* 是否对字体加粗; */)private LocalDateTime date;

       

        3、@ContentRowHeight注解
  • 作用范围:数据模型类上;
  • 注解释义:用于设置行高。

        这个注解只能加在类上面,作用就是设置单元格的高度,但这里不能像Excel那样精准设置不同行的高度,只能设置所有单元格统一的高度。

java">@ContentRowHeight(80)
        4、@ContentStyle注解
  • 作用范围:数据模型类上、字段上;
  • 注解释义:用于设置内容格式;
属性名类型功能描述
horizontalAlignmentHorizontalAlignment设置单元格内容的水平对齐方式(如左对齐、居中、右对齐)。
verticalAlignmentVerticalAlignment设置单元格内容的垂直对齐方式(如顶部对齐、中间对齐、底部对齐)。
wrappedboolean是否自动换行(true 开启自动换行)。
dataFormatshort设置单元格的数据格式(例如日期格式、数字格式等)。
fillPatternTypeFillPatternType设置单元格的填充模式(如纯色填充、斜线填充等)。
fillForegroundColorshort设置单元格的前景色(通过颜色索引表示)。
fillBackgroundColorshort设置单元格的背景色(通过颜色索引表示)。
borderLeftBorderStyle设置单元格左边框样式(如实线、虚线等)。
borderRightBorderStyle设置单元格右边框样式。
borderTopBorderStyle设置单元格顶部边框样式。
borderBottomBorderStyle设置单元格底部边框样式。
leftBorderColorshort设置单元格左边框颜色。
rightBorderColorshort设置单元格右边框颜色。
topBorderColorshort设置单元格顶部边框颜色。
bottomBorderColorshort设置单元格底部边框颜色。
java">    @ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER,/* 水平对齐方式,如居中、左对齐等; */verticalAlignment = VerticalAlignmentEnum.CENTER,/*垂直对齐方式,如上对齐、下对齐等;*/wrapped = BooleanEnum.TRUE, /* 设置文本是否应该换行(自动根据内容长度换行); */dataFormat = 0, /*数据格式,对应excel的内置数据格式; */fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND,/*设置单元格的填充模式(如纯色填充、斜线填充等)*/fillForegroundColor = 9,/*设置单元格的前景色(通过颜色索引表示)。*/fillBackgroundColor = 12,/*设置单元格的背景色(通过颜色索引表示)。*/borderLeft = BorderStyleEnum.THICK,/*设置单元格左边框样式(如实线、虚线等)。*/borderRight = BorderStyleEnum.THICK,/*设置单元格右边框样式。*/borderTop = BorderStyleEnum.THICK,/*设置单元格顶部边框样式。*/borderBottom = BorderStyleEnum.THICK,/*设置单元格底部边框样式。*/leftBorderColor = 14,/*设置单元格左边框颜色。*/rightBorderColor = 14,/*设置单元格右边框颜色。*/topBorderColor = 14,/*设置单元格顶部边框颜色。*/bottomBorderColor = 14/*设置单元格底部边框颜色。*/)

        这个注解的属性还有很多,需要的话可以自行再查阅。

        5、@HeadFontStyle注解
  • 作用范围:数据模型类上、字段上;
  • 注解释义:用于定制标题字体格式。

        这个注解的作用和可选参数,与@ContentFontStyle注解类似,不过这个注解是针对列头(表头)有效罢了。

  • 可选参数:
    • fontName:字体名称,例如 Arial宋体 等。
    • fontHeightInPoints:字体大小,以磅为单位.
    • bold:是否加粗。
    • color:字体颜色,使用 Excel 的内置颜色索引值。

        6、@HeadRowHeight注解
  • 作用范围:数据模型类上;
  • 注解释义:用于设置标题行的行高。

        此注解的作用参考@ContentRowHeight注解,当前注解只对表头生效。        

java">@HeadRowHeight(30) // 设置表头行高为 30pt
        7、@HeadStyle注解 
  • 作用范围:数据模型类上
  • 注解释义:用于设置标题样式。

        该注解的作用和可选参数参考@ContentStyle注解,但是当前注解只对表头生效。

        8、@OnceAbsoluteMerge注解
  • 作用范围:数据模型类上;
  • 注解释义:用于合并指定的单元格;
  • 可选参数:
    • firstRowIndex:从哪行开始合并;
    • lastRowIndex:到哪行结束合并;
    • firstColumnIndex:从哪列开始合并;
    • lastColumnIndex:到哪列结束合并。

        从这个注解提供的可选参数就能看出具体作用,这是通过单元格行、列索引的方式,指定生成excel文件时要合并的区域。不过要注意,使用该注解只能合并一次(对应OnceAbsoluteMerge这个合并策略类)。

java">@OnceAbsoluteMerge(firstRowIndex = 0, lastRowIndex = 1, firstColumnIndex = 0, lastColumnIndex = 1)

        9、@ContentLoopMerge注解
  • 作用范围:数据模型类的字段上;
  • 注解释义:用于合并单元格;
  • 可选参数:
    • eachRow:指定每x行合并为一行;
    • columnExtend:指定每x列合并为一列。

        该注解也是用于合并单元格的,但是可以合并多次,不过只能实现每隔n个单元格合并,使用起来限制很大,通常也不会选择通过这种注解的形式来合并单元格,这里了解即可。

四、常用读取Excel方法:

        1、同步读取所有数据后返回

        同步的返回,不推荐使用,如果数据量大会把数据放到内存里面,会影响性能。这里只做简单得举例:

java">    @PostMapping("/import")public void importExcel(MultipartFile file) throws IOException {// head: 指定读用哪个class去读// headRowNumber: 指定行头,如果行头指定错误可能会读取不到数据。如果多行头,可以设置其他值。没有指定头,也就是默认是第1行。// sheet:指定读哪个sheet页,从0开始,第一个sheet是0,第二个是1,默认就是读第一个// doReadSync: 同步读,读取完所有数据返回List<ExcelDemo> list = EasyExcel.read(file.getInputStream()).headRowNumber(1).head(ExcelDemo.class).sheet().doReadSync();for (ExcelDemo data : list) {log.info("读取到数据:{}", JSON.toJSONString(data));}}
        2、使用监听器读取所有数据

        监听器是EasyExcel常用的一个方法,监听器的好处就是可以一行一行获取数据,不用全部读完在进行处理。

        监听器示例:

java">@Slf4j
public class ExcelDemoListener extends AnalysisEventListener<ExcelDemo> {private final List<ExcelDemo> data = new ArrayList<>();/*** 每解析一条数据都会触发一次invoke()方法*/@Overridepublic void invoke(ExcelDemo excelDemo, AnalysisContext analysisContext) {log.info("成功解析到一条数据:{}", excelDemo);data.add(excelDemo);}/*** 当一个excel文件所有数据解析完成后,会触发此方法*/@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {log.info("所有数据都已解析完毕!");}public List<ExcelDemo> getData() {return data;}
}

        使用监听器读取excel文件:

java">    @PostMapping("/import")public void importExcel(MultipartFile file) throws IOException {// 监听器需要手动new出来ExcelDemoListener excelDemoListener = new ExcelDemoListener();// 将监听器放入read方法中,会走监听器内部的方法来读取数据                      EasyExcel.read(file.getInputStream(),ExcelDemo.class,excelDemoListener).sheet().doRead();List<ExcelDemo> data = excelDemoListener.getData();log.info("总共解析到到" + data.size() + "条数据!");}

        监听器内部逻辑可以自行定义,一般会设置数据上限,当数据读取到上限时,就自动批量存储到数据库中。

        3、读多个sheet

        一次读取全部sheet,需要使用  doReadAll 方法,这个方法一次读取全部sheet页,并传给监听器处理。这中适用于全部sheet页全部都是同一个实体类接收。

java">        ExcelDemoListener excelDemoListener = new ExcelDemoListener();EasyExcel.read(file.getInputStream(),ExcelDemo.class,excelDemoListener).doReadAll();List<ExcelDemo> data = excelDemoListener.getData();log.info("总共解析到到" + data.size() + "条数据!");

        读取指定的sheet页,并指定不同的实体类接收。

java">        try (ExcelReader excelReader = EasyExcel.read(file.getInputStream()).build()) {// 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的ListenerExcelDemoListener excelDemoListener = new ExcelDemoListener();ReadSheet readSheet1 =EasyExcel.readSheet(0).head(ExcelDemo.class).registerReadListener(excelDemoListener).build();ReadSheet readSheet2 =EasyExcel.readSheet(1).head(ExcelDemo.class).registerReadListener(excelDemoListener).build();// 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能excelReader.read(readSheet1, readSheet2);}

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

        需要用到上面的注解,以及自定义转换器实现类。

        5、多行头

        当读取excel时,如果行头并不是第一行,就需要配合 headRowNumber 方法指定行头是哪一行,但是如果指定的不对,会导致数据读取失败。

java">        ExcelDemoListener excelDemoListener = new ExcelDemoListener();EasyExcel.read(file.getInputStream(),ExcelDemo.class,excelDemoListener).sheet()// 默认读取第一行为表头,如果第一行不是,需要单独设置headRowNumber,从0开始.headRowNumber(1).doRead();List<ExcelDemo> data = excelDemoListener.getData();log.info("总共解析到到" + data.size() + "条数据!");
        6、读取表头数据

        只需要在监听器中实现一个方法,只要重写invokeHeadMap方法即可

java">    @Overridepublic void invokeHead(Map<Integer, ReadCellData<?>> headMap, AnalysisContext context) {log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));}@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {log.info("解析到一条头数据:{}", JSON.toJSONString(headMap));// 如果想转成成 Map<Integer,String>// 方案1: 不要implements ReadListener 而是 extends AnalysisEventListener// 方案2: 调用 ConverterUtils.convertToStringMap(headMap, context) 自动会转换}

 解析到一条头数据:{0:{"columnIndex":0,"dataFormatData":{"format":"General","index":0},"rowIndex":0,"stringValue":"姓名","type":"STRING"},1:{"columnIndex":1,"dataFormatData":{"$ref":"$[0].dataFormatData"},"rowIndex":0,"stringValue":"日期","type":"STRING"},2:{"columnIndex":2,"dataFormatData":{"$ref":"$[0].dataFormatData"},"rowIndex":0,"stringValue":"年龄","type":"STRING"},3:{"columnIndex":3,"dataFormatData":{"$ref":"$[0].dataFormatData"},"rowIndex":0,"stringValue":"薪水","type":"STRING"},4:{"columnIndex":4,"dataFormatData":{"$ref":"$[0].dataFormatData"},"rowIndex":0,"stringValue":"地址","type":"STRING"}} 

成功解析到一条数据:ExcelDemo(name=张三, age=18, salary=11111.11, address=北京, dateTime=2024-12-21T20:20:20) 

        7、额外信息(批注、超链接、合并单元格信息读取)

        一般很少用,需要可以再了解一下,这里只简单做示例:
        需要 在监听器里面多实现一个 extra 方法:

java"> @Overridepublic void extra(CellExtra extra, AnalysisContext context) {log.info("读取到了一条额外信息:{}", JSON.toJSONString(extra));switch (extra.getType()) {case COMMENT:log.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(),extra.getText());break;case HYPERLINK:if ("Sheet1!A1".equals(extra.getText())) {log.info("额外信息是超链接,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(),extra.getColumnIndex(), extra.getText());} else if ("Sheet2!A1".equals(extra.getText())) {log.info("额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{},"+ "内容是:{}",extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),extra.getLastColumnIndex(), extra.getText());} else {Assert.fail("Unknown hyperlink!");}break;case MERGE:log.info("额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}",extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),extra.getLastColumnIndex());break;default:}}
java"> // 这里 需要指定读用哪个class去读,然后读取第一个sheetEasyExcel.read(fileName, DemoExtraData.class, new DemoExtraListener())// 需要读取批注 默认不读取.extraRead(CellExtraTypeEnum.COMMENT)// 需要读取超链接 默认不读取.extraRead(CellExtraTypeEnum.HYPERLINK)// 需要读取合并单元格信息 默认不读取.extraRead(CellExtraTypeEnum.MERGE).sheet().doRead();

        8、读取公式和单元格类型

        也比较少用,需要自行钻研,只简单举例:

java">@Getter
@Setter
@EqualsAndHashCode
public class CellDataReadDemoData {private CellData<String> string;// 这里注意 虽然是日期 但是 类型 存储的是number 因为excel 存储的就是numberprivate CellData<Date> date;private CellData<Double> doubleData;// 这里并不一定能完美的获取 有些公式是依赖性的 可能会读不到 这个问题后续会修复private CellData<String> formulaValue;
}
java">   @Testpublic void cellDataRead() {String fileName = TestFileUtil.getPath() + "demo" + File.separator + "cellDataDemo.xlsx";// 这里 需要指定读用哪个class去读,然后读取第一个sheetEasyExcel.read(fileName, CellDataReadDemoData.class, new CellDataDemoHeadDataListener()).sheet().doRead();}
        9、数据转换等异常处理

        需要在监听器里面实现重写onException方法即可:

java">  @Overridepublic void onException(Exception exception, AnalysisContext context) {log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());// 如果是某一个单元格的转换异常 能获取到具体行号// 如果要获取头的信息 配合invokeHeadMap使用if (exception instanceof ExcelDataConvertException) {ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;log.error("第{}行,第{}列解析异常,数据为:{}", excelDataConvertException.getRowIndex(),excelDataConvertException.getColumnIndex(), excelDataConvertException.getCellData());}}
        10、不创建对象的读

        不创建对象的读,可以接收一个map集合,然后在监听器中自行进行转换。

java">@Slf4j
public class DataListener extends AnalysisEventListener<Map<Integer, String>> {/*** 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收*/private static final int BATCH_COUNT = 5;private List<Map<Integer, String>> dataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);@Overridepublic void invoke(Map<Integer, String> data, AnalysisContext context) {log.info("解析到一条数据:{}", JSON.toJSONString(data));dataList.add(data);if (dataList.size() >= BATCH_COUNT) {saveData();dataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {saveData();log.info("所有数据解析完成!");}/*** 加上存储数据库*/private void saveData() {log.info("{}条数据,开始存储数据库!", dataList.size());log.info("存储数据库成功!");}
}
java">        // 这里 只要,然后读取第一个sheet 同步读取会自动finishEasyExcel.read(fileName, new DataListener()).sheet().doRead();

五、常用导出Excel方法

        1、简单导出

         注意:简单导出 在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入

java">    @PostMapping("/export")public void exportExcel(HttpServletResponse response) throws IOException {// write 指定输出流,跟模板对象// sheet 指定导出sheet页名称// dowrite 指定数据源EasyExcel.write(response.getOutputStream(), ExcelDemo.class).sheet("excel模板").doWrite(this::getDatas);response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode("测试导出excel", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8" + fileName + ".xlsx");}
        2、根据参数只导出指定列

              根据参数名列表,导出指定字段,需要使用 includeColumnFieldNames 方法:

java">        //导出指定字段Set<String> exportFields = new HashSet<String>();exportFields.add("name");exportFields.add("age");// write 指定输出流,跟模板对象// sheet 指定导出sheet页名称// dowrite 指定数据源EasyExcel.write(response.getOutputStream(), ExcelDemo.class).includeColumnFieldNames(exportFields).sheet("excel模板").doWrite(getDatas());response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode("测试导出excel", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8" + fileName + ".xlsx");

        根据参数名列表,忽略指定字段,需要使用 excludeColumnFiledNames 方法:

java">        //导出指定字段Set<String> noExportFields = new HashSet<String>();noExportFields.add("name");noExportFields.add("age");// write 指定输出流,跟模板对象// sheet 指定导出sheet页名称// dowrite 指定数据源EasyExcel.write(response.getOutputStream(), ExcelDemo.class).excludeColumnFieldNames(noExportFields).sheet("excel模板").doWrite(getDatas());response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode("测试导出excel", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8" + fileName + ".xlsx");

         导出指定列的数据还可以结合 @ExcelIgnore注解 或 @ExcelIgnoreUnannotated注解 使用,来导出指定列,或者忽略某些列。

        3、复杂头写入

        使用@ExcelProperty注解即可设置:

java">@Data
@AllArgsConstructor
@NoArgsConstructor
public class ExcelDemo {@ExcelProperty({"表头1","姓名"})private String name;@ExcelProperty({"表头1","年龄"})private int age;@ExcelProperty({"薪水"})private BigDecimal salary;@ExcelProperty({"表头2","地址"})private String address;@ExcelProperty({"表头2","日期"})private LocalDateTime dateTime;}

        

         相同表头会合并。

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

        重复写入同一个sheet页:

java">        // 方法1: 如果写到同一个sheet// 这里 需要指定写用哪个class去写try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ExcelDemo.class).build()) {// 这里注意 如果同一个sheet只要创建一次WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来for (int i = 0; i < 5; i++) {// 分页去数据库查询数据 这里可以去数据库查询每一页的数据List<ExcelDemo> data = getDatas();excelWriter.write(data, writeSheet);}}response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode("测试导出excel", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8" + fileName + ".xlsx");

        写入不同得sheet页,同一个对象:

java">        // 方法2: 如果写到不同的sheet 同一个对象// 这里 指定文件try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ExcelDemo.class).build()) {// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面for (int i = 0; i < 5; i++) {// 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).build();// 分页去数据库查询数据 这里可以去数据库查询每一页的数据List<ExcelDemo> data = getDatas();excelWriter.write(data, writeSheet);}}

        写入不同的sheet页,不同的对象:

java">        // 方法3 如果写到不同的sheet 不同的对象// 这里 指定文件try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build()) {// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面for (int i = 0; i < 5; i++) {// 每次都要创建writeSheet 这里注意必须指定sheetNo 而且sheetName必须不一样。这里注意DemoData.class 可以每次都变,我这里为了方便 所以用的同一个class// 实际上可以一直变WriteSheet writeSheet = EasyExcel.writerSheet(i, "模板" + i).head(ExcelDemo.class).build();// 分页去数据库查询数据 这里可以去数据库查询每一页的数据List<ExcelDemo> data = getDatas();excelWriter.write(data, writeSheet);}}

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

        日期、数字、自定义格式转行,可以结合 注解:
        @DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
        @NumberFormat("#.##%")
        Converter 自定义转换器
        上面写了,不再举例

       

        6、图片导出

        7、超链接、备注、公式、指定单个单元格的样式、单个单元格多种样式

        8、根据模板写入

        我得理解就是,在已经提供了一份excel文件中,继续导入数据,例如我们现在有一份excel文件如下所示:

        
        现在我们要以这个文件为模板,在这个文件得基础上继续导出数据,会得到如下所示:
       

        导出代码:

java">       String templateFileName = "C:\\Users\\Administrator\\Desktop\\ces\\" + "response111.xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 这里要注意 withTemplate 的模板文件会全量存储在内存里面,所以尽量不要用于追加文件,如果文件模板文件过大会OOM// 如果要再文件中追加(无法在一个线程里面处理,可以在一个线程的建议参照多次写入的demo) 建议临时存储到数据库 或者 磁盘缓存(ehcache) 然后再一次性写入EasyExcel.write(response.getOutputStream(), ExcelDemo.class).withTemplate(templateFileName).sheet().doWrite(getDatas());
         9、列宽、行高

        需要结合注解: 
        @ContentRowHeight(10) :设置行高为10px
        @HeadRowHeight(20):设置标题行的行高为20px
        @ColumnWidth(25):列的宽度为25px

java">@Data
@AllArgsConstructor
@NoArgsConstructor
@ContentRowHeight(10)
@HeadRowHeight(20)
@ColumnWidth(25)
public class ExcelDemo {@ExcelProperty({"表头1","姓名"})private String name;@ExcelProperty({"表头1","年龄"})private int age;@ExcelProperty({"薪水"})private BigDecimal salary;@ExcelProperty({"表头2","地址"})private String address;@ExcelProperty({"表头2","日期"})private LocalDateTime dateTime;
}
        10、注解形式自定义样式

        需要结合上面讲述得注解:

java">@Data
@AllArgsConstructor
@NoArgsConstructor
// 头背景设置成红色 IndexedColors.RED.getIndex()
@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 10)
// 头字体设置成20
@HeadFontStyle(fontHeightInPoints = 20)
// 内容的背景设置成绿色 IndexedColors.GREEN.getIndex()
@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 17)
// 内容字体设置成20
@ContentFontStyle(fontHeightInPoints = 20)
public class ExcelDemo {// 字符串的头背景设置成粉红 IndexedColors.PINK.getIndex()@HeadStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 14)// 字符串的头字体设置成20@HeadFontStyle(fontHeightInPoints = 30)// 字符串的内容的背景设置成天蓝 IndexedColors.SKY_BLUE.getIndex()@ContentStyle(fillPatternType = FillPatternTypeEnum.SOLID_FOREGROUND, fillForegroundColor = 40)// 字符串的内容字体设置成20@ContentFontStyle(fontHeightInPoints = 30)@ExcelProperty({"表头1","姓名"})private String name;@ExcelProperty({"表头1","年龄"})private int age;@ExcelProperty({"薪水"})private BigDecimal salary;@ExcelProperty({"表头2","地址"})private String address;@ExcelProperty({"表头2","日期"})private LocalDateTime dateTime;}

        一般只会设置一些简单样式,具体需要可以自行查询相应注解。

        11、使用内置策略模式设置自定义样式

         EasyExcel中提供了两个样式策略,用来是设置导出文件得样式,只需要简单配置即可使用:
        HorizontalCellStyleStrategy:允许设置每一行的样式一致,或者隔行样式一致。
        AbstractVerticalCellStyleStrategy:使用这个策略时,可以为每一列单独设置样式,需要通过回调函数来定义不同列的样式。

         设置每一行得样式:

        HorizontalCellStyleStrategy 方法,一般接收两个 WriteCellStyle 类型得参数,第一个是设置表头格式得,第二个是设置内容格式的。
        WriteCellStyle 是 EasyExcel 中用于设置 Excel 单元格样式的类。每个属性用于定义单元格的不同样式,包括字体、边框、填充颜色、对齐方式等。下面是对每个参数详细得简单的讲解:

java">        WriteCellStyle cellStyle = new WriteCellStyle();// 1. DataFormatData 设置数据格式// 这里假设你设置为数字格式, 示例代码中未使用具体的 DataFormatData// cellStyle.setDataFormatData(new DataFormatData("0.00"));// 2. WriteFont 设置字体WriteFont font = new WriteFont();font.setFontHeightInPoints((short) 12);  // 字体大小为 12font.setBold(true);  // 设置加粗font.setFontName("Arial");  // 字体设置为 ArialcellStyle.setWriteFont(font);// 3. hidden 设置隐藏cellStyle.setHidden(false);  // 设置为可见// 4. locked 设置是否锁定cellStyle.setLocked(true);  // 设置单元格为锁定,不能编辑// 5. quotePrefix 设置前缀引号cellStyle.setQuotePrefix(true);  // 设置文本前加引号// 6. HorizontalAlignment 设置水平对齐cellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);  // 设置为居中对齐// 7. wrapped 设置是否换行cellStyle.setWrapped(true);  // 设置文本自动换行// 8. VerticalAlignment 设置垂直对齐cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);  // 设置为垂直居中// 9. rotation 设置文本旋转cellStyle.setRotation((short) 90);  // 设置文本旋转 90 度// 10. indent 设置缩进cellStyle.setIndent((short) 2);  // 设置缩进为 2 个字符// 11-14. BorderStyle 设置边框样式cellStyle.setBorderLeft(BorderStyle.THIN);  // 设置左边框为细边框cellStyle.setBorderRight(BorderStyle.MEDIUM);  // 设置右边框为中等边框cellStyle.setBorderTop(BorderStyle.THICK);  // 设置顶部边框为粗边框cellStyle.setBorderBottom(BorderStyle.DOTTED);  // 设置底部边框为虚线// 15-18. 边框颜色设置cellStyle.setLeftBorderColor(IndexedColors.BLACK.getIndex());  // 左边框颜色为黑色cellStyle.setRightBorderColor(IndexedColors.BLUE.getIndex());  // 右边框颜色为蓝色cellStyle.setTopBorderColor(IndexedColors.GREEN.getIndex());  // 顶部边框颜色为绿色cellStyle.setBottomBorderColor(IndexedColors.RED.getIndex());  // 底部边框颜色为红色// 19. FillPatternType 设置填充模式cellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);  // 设置为实心填充模式// 20-21. 背景颜色和前景颜色设置cellStyle.setFillBackgroundColor(IndexedColors.YELLOW.getIndex());  // 设置背景色为黄色cellStyle.setFillForegroundColor(IndexedColors.ORANGE.getIndex());  // 设置前景色为橙色// 22. shrinkToFit 设置是否自适应缩小cellStyle.setShrinkToFit(true);  // 设置文本自适应缩小

        简单使用:

java">        //行头字体WriteFont headFont = new WriteFont();headFont.setFontName("华文楷体");headFont.setFontHeightInPoints((short) 18);headFont.setBold(true);//内容字体WriteCellStyle cellStyle = createCellStyle();WriteFont contentFont = new WriteFont();contentFont.setFontName("宋体");contentFont.setFontHeightInPoints((short) 10);contentFont.setBold(false);//行头设置WriteCellStyle headCellStyle = new WriteCellStyle();headCellStyle.setWriteFont(headFont);headCellStyle.setFillForegroundColor(IndexedColors.WHITE1.getIndex());headCellStyle.setBorderTop(BorderStyle.THIN);headCellStyle.setBorderBottom(BorderStyle.THIN);headCellStyle.setBorderLeft(BorderStyle.THIN);headCellStyle.setBorderRight(BorderStyle.THIN);headCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);//内容设置WriteCellStyle contentCellStyle = new WriteCellStyle();contentCellStyle.setWriteFont(contentFont);contentCellStyle.setBorderTop(BorderStyle.THIN);contentCellStyle.setBorderBottom(BorderStyle.THIN);contentCellStyle.setBorderLeft(BorderStyle.THIN);contentCellStyle.setBorderRight(BorderStyle.THIN);contentCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);HorizontalCellStyleStrategy horizontalCellStyleStrategy = new HorizontalCellStyleStrategy(headCellStyle, cellStyle);EasyExcel.write(response.getOutputStream(), ExcelDemo.class).registerWriteHandler(horizontalCellStyleStrategy).sheet("模板").doWrite(getDatas());

        

        设置列的样式:

  AbstractVerticalCellStyleStrategy 是一个抽象类,必须通过继承并重写方法来实现自定义的列样式。主要是重写下面两个方法:

       headCellStyle(Head head)          ----设置标题栏的列样式
       contentCellStyle(Head head)     ----设置表格内容的列样式

       具体实现代码:
       先实现重写AbstractVerticalCellStyleStrategy

java">public class CustomVerticalCellStyleStrategy extends AbstractVerticalCellStyleStrategy {// 重写定义表头样式的方法@Overrideprotected WriteCellStyle headCellStyle(Head head) {WriteCellStyle writeCellStyle = new WriteCellStyle();if(head.getColumnIndex() == 0) {//设置行头的第一列  单元格水平居中writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);}else if(head.getColumnIndex() == 1){//设置行头的第二列  单元格水平居左writeCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);}else if(head.getColumnIndex() == 2){//设置行头的第三列  单元格水平居右writeCellStyle.setHorizontalAlignment(HorizontalAlignment.RIGHT);}else if(head.getColumnIndex() == 3){//设置行头大于第三列  单元格水平居右writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);}return writeCellStyle;}// 重写定义内容部分样式的方法@Overrideprotected WriteCellStyle contentCellStyle(Head head) {WriteCellStyle writeCellStyle = new WriteCellStyle();writeCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);writeCellStyle.setFillBackgroundColor(IndexedColors.GREEN.getIndex());writeCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);return writeCellStyle;}}

        再将自定义样式策略注册到Excel中:

java">        //数据表格的自定义列样式CustomVerticalCellStyleStrategy customVerticalCellStyleStrategy= new CustomVerticalCellStyleStrategy();// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭EasyExcel.write(response.getOutputStream(), ExcelDemo.class).registerWriteHandler(customVerticalCellStyleStrategy).sheet("模板").doWrite(getDatas());

        上面是简单的示例,具体每一列的样式,可以根据自己的需要定义。

        12、自定义单元格

        通过实现 CellWriteHandler 接口,可以完全自定义单元格的写入行为和样式。

        CellWriteHandler 接口,继承自 WriteHandler,用于在 Excel 写入过程中处理单元格的不同生命周期阶段。每个方法都处理特定的操作,通常用于 Excel 文件的写入时,针对单元格的创建、数据转换、处理和销毁等环节进行自定义处理。

        这个接口的作用是提供钩子方法(默认方法)来让用户在写入数据到 Excel 时,能够在每个关键阶段插入自己的逻辑。以下是对每个方法的详细解释:
 

  • 1. beforeCellCreate(CellWriteHandlerContext context)
    • 作用:在单元格创建之前调用,用于在写入单元格之前进行一些准备工作。
    • 参数:方法内部通过 context 参数提取了多个信息,包括 WriteSheetHolder(写入的工作表)、WriteTableHolder(写入的表格)、Row(当前行)、Head(表头信息)、columnIndex(列索引)、relativeRowIndex(相对行索引)、isHead(是否是表头)等。
    • 默认实现:这个方法默认调用了另一个重载版本 beforeCellCreate(WriteSheetHolder, WriteTableHolder, Row, Head, Integer, Integer, Boolean),该重载方法的默认实现为空,表示没有默认操作,用户可以在实现接口时进行自定义。
  • 2. beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead)
    • 作用:实际执行创建单元格之前的处理逻辑的方法。可以根据需要对 Excel 单元格的创建过程进行自定义操作,如设置格式、填充数据等。
    • 参数:接受了各个具体的参数,允许用户在不同的上下文中访问这些信息。
  • 3. afterCellCreate(CellWriteHandlerContext context)
    • 作用:在单元格创建之后调用。这个方法在单元格已经创建后执行,可以用来处理与单元格创建相关的操作,例如设置样式或处理其他后续步骤。
    • 参数:同样是通过 context 提供相关的信息,包括工作表、表格、当前单元格、表头信息、相对行索引等。
    • 默认实现:默认调用了另一个重载版本 afterCellCreate(WriteSheetHolder, WriteTableHolder, Cell, Head, Integer, Boolean),该重载方法默认没有实现任何逻辑,允许用户进行自定义。
  • 4. afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead)
    • 作用:执行单元格创建后的一些自定义处理逻辑。与 beforeCellCreate 方法类似,但在单元格已创建之后进行操作。
    • 参数:该方法接受详细的参数,允许用户访问到当前工作表、单元格、表头等信息。
  • 5. afterCellDataConverted(CellWriteHandlerContext context)
    • 作用:在单元格的数据被转换(例如格式化、数据类型转换)之后调用。这个方法允许用户在数据转换后进行一些额外的处理,比如修改转换后的数据或进一步调整单元格的内容。
    • 参数:context.getCellDataList() 提供了当前单元格的数据,方法会根据需要选择第一个数据进行处理(假设该列表不为空)。其他参数和前面的钩子方法类似。
  • 6. afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, WriteCellData<?> cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead)
    • 作用:执行数据转换后的处理逻辑。用户可以在此时进一步调整数据内容、修改样式或执行其他操作。
    • 参数:cellData 是转换后的数据,允许用户访问和操作。
  • 7. afterCellDispose(CellWriteHandlerContext context)
    • 作用:在单元格被销毁之前调用。这个方法是在单元格生命周期的最后阶段,用于清理或执行一些最终的操作。通常会在单元格数据处理和格式化完成后进行一些额外的操作,如记录日志或执行清理工作。
    • 参数:context.getCellDataList() 提供了当前单元格的所有数据列表,用户可以在此进行处理。
  • 8. afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead)
    • 作用:处理单元格销毁之后的操作。此方法接收了与前面方法相同的详细参数,允许用户在最后一步进行操作。
    • 参数:cellDataList 包含了所有需要处理的单元格数据,cell 是当前单元格,head 是表头信息,isHead 是一个布尔值,指示是否为表头行        

        总结:
        这个接口主要用于对 Excel 写入过程中的每个关键步骤提供自定义的处理机制。通过实现CellWriteHandler 接口,用户可以在:

  • 单元格创建之前 (beforeCellCreate)
  • 单元格创建之后 (afterCellCreate)
  • 数据转换之后 (afterCellDataConverted)
  • 单元格销毁之前 (afterCellDispose)

        等多个阶段进行干预和扩展,实现自定义的单元格处理逻辑。例如,可以用于设置单元格样式、数据格式化、数据验证、日志记录等。

        实际上上述的方法都可以进行单元格的样式设置,但是每个方法代表不同的时期,不同的时期能获取到的信息不同,所以可以选择合适的方法来进行自定义格式,但是其实如果不是有复杂的需求,其实不推荐使用该方法进行自定义单元格样式。

        简单示例:

java">        EasyExcel.write(response.getOutputStream(), ExcelDemo.class).registerWriteHandler(new CellWriteHandler() {@Overridepublic void afterCellDispose(CellWriteHandlerContext context) {// 当前事件会在 数据设置到poi的cell里面才会回调// 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not trueif (BooleanUtils.isNotTrue(context.getHead())) {// 第一个单元格// 只要不是头 一定会有数据 当然fill的情况 可能要context.getCellDataList() ,这个需要看模板,因为一个单元格会有多个 WriteCellDataWriteCellData<?> cellData = context.getFirstCellData();// 这里需要去cellData 获取样式// 很重要的一个原因是 WriteCellStyle 和 dataFormatData绑定的 简单的说 比如你加了 DateTimeFormat// ,已经将writeCellStyle里面的dataFormatData 改了 如果你自己new了一个WriteCellStyle,可能注解的样式就失效了// 然后 getOrCreateStyle 用于返回一个样式,如果为空,则创建一个后返回WriteCellStyle writeCellStyle = cellData.getOrCreateStyle();writeCellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUNDwriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);// 这样样式就设置好了 后面有个FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到 cell里面去 所以可以不用管了}}}).sheet("模板").doWrite(getDatas());

        使用poi的样式设置,用的很少也不推荐使用,只做简单使用:

java">// 方法3: 使用poi的样式完全自己写 不推荐// @since 3.0.0-beta2// 坑1:style里面有dataformat 用来格式化数据的 所以自己设置可能导致格式化注解不生效// 坑2:不要一直去创建style 记得缓存起来 最多创建6W个就挂了fileName = TestFileUtil.getPath() + "handlerStyleWrite" + System.currentTimeMillis() + ".xlsx";EasyExcel.write(fileName, DemoData.class).registerWriteHandler(new CellWriteHandler() {@Overridepublic void afterCellDispose(CellWriteHandlerContext context) {// 当前事件会在 数据设置到poi的cell里面才会回调// 判断不是头的情况 如果是fill 的情况 这里会==null 所以用not trueif (BooleanUtils.isNotTrue(context.getHead())) {Cell cell = context.getCell();// 拿到poi的workbookWorkbook workbook = context.getWriteWorkbookHolder().getWorkbook();// 这里千万记住 想办法能复用的地方把他缓存起来 一个表格最多创建6W个样式// 不同单元格尽量传同一个 cellStyleCellStyle cellStyle = workbook.createCellStyle();cellStyle.setFillForegroundColor(IndexedColors.RED.getIndex());// 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUNDcellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);cell.setCellStyle(cellStyle);// 由于这里没有指定dataformat 最后展示的数据 格式可能会不太正确// 这里要把 WriteCellData的样式清空, 不然后面还有一个拦截器 FillStyleCellWriteHandler 默认会将 WriteCellStyle 设置到// cell里面去 会导致自己设置的不一样context.getFirstCellData().setWriteCellStyle(null);}}}).sheet("模板").doWrite(data());

        13、合并单元格

        可以配合注解:
        @ContentLoopMerge
        @OnceAbsoluteMerge
        来进行单元格的合并。

java">// 将第6-7行的2-3列合并成一个单元格@OnceAbsoluteMerge(firstRowIndex = 5, lastRowIndex = 6, firstColumnIndex = 1, lastColumnIndex = 2)
public class ExcelDemo {// 这一列 每隔2行 合并单元格@ContentLoopMerge(eachRow = 2)@ExcelProperty({"姓名"})private String name;@ExcelProperty({"年龄"})private int age;@ExcelProperty({"薪水"})private BigDecimal salary;@ExcelProperty({"地址"})private String address;@ExcelProperty({"日期"})private LocalDateTime dateTime;}


http://www.ppmy.cn/ops/150993.html

相关文章

【MySQL】高级查询技巧 JOIN、GROUP BY、ORDER BY、UNION 应用案列解析

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《MySQL技术精粹》&#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、MySQL起源 2、MySQL应用场景 二、MySQL高级查询技巧 1、连接查询&am…

npm发布组件(vue3+webpack)

1.初始化Vue项目 vue create my-app 2.本地运行 npm run serve 3.新增目录和文件 1. src/package/index.js 2. src/package/wlz-btn/index.vue 3. src/package/wlz-input/index.vue // src\package\index.js import WlzBtn from "./wlz-btn"; import WlzInput …

海康MV-EB435i立体相机SDK安装(ROS 2)

文章目录 一、简介二、驱动配置小结 一、简介 MV-EB435i相机是一款低成本、小体积、配置全面的立体相机&#xff0c;凭借硬件级的深度图像处理方案&#xff0c;相机可在高性能输出的同时维持低功耗的水平。相机采用海康MV3D SDK&#xff0c;并提供跨平台支持&#xff0c;广泛应…

【MyDB】3-DataManager数据管理 之 4-数据页缓存

【MyDB】3-DataManager数据管理 之 3-数据页管理 页面缓存设计与实现PageImpl页面定义getForCache() 文件中读取页面数据releaseForCache() 驱逐页面AtomicInteger 记录当前打开数据库文件页recoverInsert()和recoverUpdate() 参考资料 本章涉及代码&#xff1a;top/xianghua/m…

工业视觉2-相机选型

工业视觉2-相机选型 一、按芯片类型二、按传感器结构特征三、按扫描方式四、按分辨率大小五、按输出信号六、按输出色彩接口类型 这张图片对工业相机的分类方式进行了总结&#xff0c;具体如下&#xff1a; 一、按芯片类型 CCD相机&#xff1a;采用电荷耦合器件&#xff08;CC…

【PGCCC】PostgreSQL 临时文件的使用

临时文件 某些查询操作&#xff08;例如sort或hash表&#xff09;需要一些内存功能。此内存由运行时配置提供work_mem。 来自官方文档work_mem work_mem (整数) 设置在写入临时磁盘文件之前查询操作&#xff08;例如排序或哈希表&#xff09;使用的基本最大内存量。 请注意&…

C# OpenCV机器视觉:图片去水印

阿强是个不折不扣的动漫迷&#xff0c;最近他疯狂迷上了一部超火的老动漫&#xff0c;每天茶不思饭不想&#xff0c;心心念念就盼着能多看几集。然而&#xff0c;他在网上找到的资源却像是调皮孩子脸上的脏手印&#xff0c;布满了各种乱七八糟的水印&#xff0c;这可把阿强给郁…

HTTPS与HTTP:区别及安全性对比

目录 一、基础概念 二、安全性对比 1. 加密传输 2. 身份验证 3. 数据完整性 4. 端口 5. 浏览器展示方式 三、使用场景与性能 1. 使用场景 2. 性能开销 四、成本与维护 五、搜索引擎优化&#xff08;SEO&#xff09; 六、案例分析 七、隐私保护与中间人攻击 八、…