SpringBoot集成阿里EasyExcel导出excel高级实战

news2025/1/6 20:43:16

目录

  • 参考
  • 一、引入包
  • 二、导出到文件并输出到后台
  • 三、过滤字段
    • 方式1:类上加注解 @ExcelIgnoreUnannotated,过滤属性没有@ExcelProperty注解的字段
    • 方式2:指定字段加注解
    • 方式3:代码指定过滤字段, 同一个excel生成两个sheet分别过滤不同字段
  • 四、冻结列
    • 冻结列, 冻结姓名列
    • 注册handler
  • 五、格式化
    • 把保留2位小数
    • 统一数字转换器
    • 枚举转换器
    • 自定义格式转换器
    • 自定义转换器
    • 配置excel导出模型
  • 六、导出
    • 通过controller导出
    • 统一导出转换器导出
  • 七、效果

参考

easyexcel使用教程-导出篇

一、引入包


<!--easyexcel -->
 <dependency>
     <groupId>com.alibaba</groupId>
     <artifactId>easyexcel</artifactId>
     <version>2.2.6</version>
 </dependency>

二、导出到文件并输出到后台



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

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;

/**
 * excel导出
 * @create 2022-12-09
 */
public class ExcelUtil {

    private ExcelWriter excelWriter;
    private  File file;
    private String fileName;
    private HttpServletResponse response;
    private WriteSheet writeSheet;


    @SneakyThrows
    public static <T> ExcelUtil create(HttpServletResponse response,String fileNamePrefix,  Class<T> excelModeClass){
        ExcelUtil excelUtil = new ExcelUtil();
        excelUtil.response = response;
        excelUtil.fileName = fileNamePrefix+ ".xlsx";
        // 临时文件
        String filePath =  "/" +fileNamePrefix+ System.currentTimeMillis() + ".xlsx";
        excelUtil.file = new File(filePath);
            // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        excelUtil.excelWriter = EasyExcel.write(filePath, excelModeClass).build();
        excelUtil.writeSheet = EasyExcel.writerSheet("第一页").build();

            // 写入数据
        return excelUtil;
    }

    @SneakyThrows
    public void export(){
            ServletOutputStream out = response.getOutputStream();

            // 千万别忘记finish 会帮忙关闭流
            excelWriter.finish();

            // 导出
            String fileName = new String(this.fileName
                    .getBytes(StandardCharsets.UTF_8), "iso8859-1");
            response.setHeader("Content-disposition", "attachment;filename="+fileName);
            response.setContentType("multipart/form-data");
            response.setCharacterEncoding("utf-8");
            //4.获取要下载的文件输入流
            InputStream in = Files.newInputStream(Paths.get(this.file.getPath()));
            int len;
            //5.创建数据缓冲区
            byte[] buffer = new byte[1024];
            //6.通过response对象获取OutputStream流
            //7.将FileInputStream流写入到buffer缓冲区
            while ((len = in.read(buffer)) > 0) {
                //8.使用OutputStream将缓冲区的数据输出到客户端浏览器
                out.write(buffer,0,len);
            }
            in.close();
            this.file.deleteOnExit();
            out.flush();
    }


    @SneakyThrows
    public <T> void writeData(List<T> data){
        excelWriter.write(data, writeSheet);
    }
}

三、过滤字段

过滤字段不生成excel

方式1:类上加注解 @ExcelIgnoreUnannotated,过滤属性没有@ExcelProperty注解的字段

@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor  // 一定要有无参构造方法
@ExcelIgnoreUnannotated
public class Student {
    .....
}

方式2:指定字段加注解

@ExcelIgnore // demo2不生成excel
private String demo2;

方式3:代码指定过滤字段, 同一个excel生成两个sheet分别过滤不同字段

/**
 * 过滤字段
 */
@Test
public void exportExcludeColumn() {
    Consumer<ExcelWriter> consumer = writer ->
            writer.write(generateStudent(10), EasyExcel.writerSheet(1, "学生信息")
                    .excludeColumnFiledNames(Arrays.asList("name", "sex")) // sheet1过滤姓名、性别
                    .head(Student.class)
                    .build());
    consumer = consumer.andThen(writer ->
            writer.write(generateStudent(10), EasyExcel.writerSheet(2, "学生信息2")
                    .excludeColumnFiledNames(Arrays.asList("birthday", "weight")) // sheet2过滤生日和体重
                    .head(Student.class)
                    .build()));
    export("D:/报表.xlsx", consumer);

四、冻结列

冻结列, 冻结姓名列

冻结列handler,FreezeNameHandler.java

package com.learning.easyexcel.converter;

import com.alibaba.excel.write.handler.SheetWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
import org.apache.poi.ss.usermodel.Sheet;

/**
 * 冻结姓名列
 */
public class FreezeNameHandler implements SheetWriteHandler {
    @Override
    public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {

    }

    @Override
    public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
        // 获取到当前的sheet
        Sheet sheet = writeSheetHolder.getSheet();
        /**
         *第一个参数:冻结的列数
         *第二个参数:冻结的行数
         *第三个参数:冻结后第一列的列号
         *第四个参数:冻结后第一行的行号
         **/
        sheet.createFreezePane(1, 0, 1, 0);
    }
}

注册handler

  /**
     * 冻结姓名列
     */
    @Test
    public void exportFreezeColumn() {
        Consumer<ExcelWriter> consumer = writer -> {
            writer.write(generateStudent(10), EasyExcel.writerSheet("学生信息")
                    .registerWriteHandler(new FreezeNameHandler()) // 冻结姓名列
                    .head(Student.class)
                    .build());
        };
        export("D:/报表.xlsx", consumer);

五、格式化

把保留2位小数

  • 方法1,@NumberFormat 注解。修改Student类,如下做法会以字符串导出到excel,单元格靠左
@ExcelProperty(value = "体重KG")
@NumberFormat("0.##") // 会以字符串形式生成单元格,要计算的列不推荐
private BigDecimal weight;
  • 方法2:@ContentStyle(dataFormat = 2) 注解 ,我们新建一个字段weight2,会以数字导出,单元格中靠右

@ContentStyle(dataFormat = 2)
private BigDecimal weight2;

统一数字转换器

package com.test.easyexcel.converter;

import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class BigDecimalConverter implements Converter<BigDecimal> {

    @Override
    public Class supportJavaTypeKey() {
        return BigDecimal.class;
    }

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

    @Override
    public BigDecimal convertToJavaData(CellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        return cellData.getNumberValue();
    }

    @Override
    public CellData convertToExcelData(BigDecimal value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {
        return new CellData(value.setScale(2, RoundingMode.DOWN));
    }

枚举转换器

public class StatusConverter implements Converter<Integer> {

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

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

    @Override
    public Integer convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) {
        return "正常".equals(cellData.getStringValue()) ? 1 : 0;
    }

    @Override
    public CellData<String> convertToExcelData(Integer integer, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration)  {
        return new CellData<>(integer.equals(1) ? "正常" : "异常");
    }
}

自定义格式转换器

@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcelPropertyExt {
    String expression() default "";
}

自定义转换器


import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.property.ExcelContentProperty;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;

/**
 * easyExcel枚举转换
 * @create 2022-12-07
 */
public class CommonIntegerConverter implements Converter<Integer> {

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

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

    @Override
    public Integer convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) {
        return "正常".equals(cellData.getStringValue()) ? 1 : 0;
    }

    @Override
    public CellData<String> convertToExcelData(Integer integer, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration)  {
        String value = integer.toString();
        Field field = excelContentProperty.getField();
        ExcelPropertyExt annotation = field.getAnnotation(ExcelPropertyExt.class);
        if(annotation != null){
            Method[] meth = annotation.annotationType().getDeclaredMethods();
            for(Method me : meth){
                if(!me.isAccessible()){
                    me.setAccessible(true);
                }
                try {
                    //给字段重新赋值
                    String expression = (String) me.invoke(annotation);
                    List<String> list =  StringUtil.split(expression, SymbolConstants.SEMICOLON);
                    for(String dic : list){
                        List<String> items = StringUtil.split(dic, SymbolConstants.C_COMMA);
                        String key = items.get(0);
                        String v = items.get(1);
                        if(value.equals(key)) {
                            value = v;
                            break;
                        }
                    }
//                    System.out.println("expression:"+expression);
                } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
        return new CellData<>(value);
    }
}

配置excel导出模型

@Data
@AllArgsConstructor
@NoArgsConstructor
public class DataModelDTO implements Serializable {

    private static final long serialVersionUID = 15353L;

    @ExcelProperty(value = "设备名称")
    private String deviceName;

    @ExcelPropertyExt(expression = "0,停止;1,上;2,下")
    @ExcelProperty(value = "运行方向",converter = CommonIntegerConverter.class)
    private Integer direction;

    @ExcelProperty(value = "状态", converter = StatusConverter.class)
    private Integer Status;

    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")
    @ExcelProperty(value = "数据上报时间")
    private Date timestamp;
}

六、导出

通过controller导出

@Operation(summary = "导出")
@GetMapping("/data/export")
void export(HttpServletResponse response) {
    ExcelUtil excelUtil = ExcelUtil.create(response,"设备实时数据", DataModelDTO.class);
    // 写入数据
	excelUtil.writeData( getData());
	        // 导出
	excelUtil.export();
}

private List<UserDataModelDTO> getData(){
	List<UserDataModelDTO> list = new ArrayList<>();
	DataModelDTO data1 = new DataModelDTO("设备1",1,1,new Date());
	DataModelDTO data2 = new DataModelDTO("设备2",1,1,new Date());
	list.add(data1);
	list.add(data2);
	return list ;
}
        

统一导出转换器导出

Consumer<ExcelWriter> consumer = writer -> {
            writer.write(generateStudent(10), EasyExcel.writerSheet("学生信息")
                    .registerConverter(new BigDecimalConverter())
                    .head(Student.class)
                    .build());
        };
        export("D:/报表.xlsx", consumer);

七、效果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/84250.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

clickhouse集群搭建

一、clickhouse单机部署 clickhouse镜像下载地址 https://mirrors.tuna.tsinghua.edu.cn/clickhouse/ 1、关闭防火墙 #查看防火墙状态systemctl status firewalld#关闭防火墙systemctl stop firewalld#设置防火墙服务开机不自启systemctl disable firewalld#再次查看防火墙检…

开源软件如何使企业和业务受益

在当今技术驱动、快速发展的商业环境中&#xff0c;越来越多的公司选择投资开源软件。开源为企业提供了许多优势&#xff1a;更快的创新步伐、来自庞大而热情的社区的强大支持以及不受供应商锁定的影响。 对于许多人来说&#xff0c;开源不仅仅是编码。它代表了一种生活方式。…

使用IDEA工具,通过Java API 操作 HDFS (文件/目录的操作,含源码,详细操作步骤)

文章目录一&#xff0c;了解 HDFS Java API&#xff08;一&#xff09;HDFS常见类与接口&#xff08;二&#xff09;FileSystem的常用方法二&#xff0c;编写Java程序访问HDFS01 创建Maven项目02 添加相关依赖03 创建日志属性文件&#xff08;1&#xff09;在resources目录里创…

2022年还剩半个月,我从外包公司离职了...

今天是12.12&#xff0c;距离元旦也仅剩半个月&#xff0c;大家都在考论假期怎么过&#xff0c;没错&#xff0c;我离职了... 19年大专毕业通过校招进入了一家外包公司&#xff0c;干了接近3年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff…

MyBatis 注解开发

文章目录一、单表查询1. Select 注解2. Insert 注解3. Update 注解4. Delete 注解5. Param 注解二、多表关联查询1. 一对一查询2. 一对多查询3. 多对多查询一、单表查询 1. Select 注解 前面的章节介绍了 MyBatis 的基本用法、关联映射、动态 SQL 和缓存机制等知识&#xff0…

[附源码]计算机毕业设计房屋租赁系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis MavenVue等等组成&#xff0c;B/S模式…

算法竞赛入门【码蹄集进阶塔335题】(MT2201-2225)

算法竞赛入门【码蹄集进阶塔335题】(MT2201-2225&#xff09; 文章目录算法竞赛入门【码蹄集进阶塔335题】(MT2201-2225&#xff09;前言为什么突然想学算法了&#xff1f;为什么选择码蹄集作为刷题软件&#xff1f;目录1. MT2201 字符串转换2. MT2202 Summer Pockets3. MT2203…

alsa框架与音频芯片移植基础

ALSA音频框架 Alsa是Advanced Linux Sound Architecture的缩写&#xff0c;即高级Linux声音架构&#xff0c;在Linux操作系统上提供了对音频和MIDI的支持。在Linux 2.6的内核版本后&#xff0c;Alsa目前已经成为了linux的主流音频体系结构。 除了 alsa-driver&#xff0c;ALS…

CSS -- 使用纯CSS绘制三角形及常见案例汇总

文章目录1. 绘制原理2. 绘制最简单的三角形3. 绘制对话框4. 绘制两条直角边不等长的三角形1. 绘制原理 网页中常见一些三角形&#xff0c;使用 CSS 直接画出来就可以&#xff0c;不必做成图片或者字体图标 给每个边框不同的颜色可以看到&#xff0c;每个边框其实都是一个小三…

测试(缺陷管理)

目录 1.缺陷管理理论 1.1.定义 1.2缺陷的名称 1.3.产生缺陷的原因 1.4.缺陷管理流程 1.5.缺陷管理生命周期与状态 2.如何提交缺陷 2.1.提交的Bug的目的 2.2.提交Bug的注意事项 2.3.提交Bug报告应遵循的原则 2.4.Bug报告需包含哪些内容 1.缺陷管理理论 1.1.定义 软件…

rust编程-rust所有权理解(chapter 4.3 Slice切片类型)

目录 3. 切片&#xff08;Slice&#xff09;类型 3.1 String slice(字符串切片) 3.2 其它切片 3. 切片&#xff08;Slice&#xff09;类型 切片可以用来获取一个集合中连续的元素序列&#xff0c;且切片是一种引用类型&#xff0c;因此不具有所有权。 如下是一个小的编程示…

day17_面向对象的三大特征之一(多态)

概述 多态是继封装、继承之后&#xff0c;面向对象的第三大特性。 生活中&#xff0c;比如求面积的功能&#xff0c;圆、矩形、三角形实现起来是不一样的。跑的动作&#xff0c;小猫、小狗和大象&#xff0c;跑起来是不一样的。再比如飞的动作&#xff0c;昆虫、鸟类和飞机&a…

【数字IC设计】Design Compiler入门

本博客参考自文章链接 本文以全加器为例&#xff0c;演示DC综合的流程。设计文件如下&#xff1a; module full_adder( input clk, input rstn, input [31:0] a_in, input [31:0] b_in, input c_in, output reg [31:0] sum_out, output reg c_out ); wire c_out_w; wire [31:0…

[vue学习笔记]数组+事件+v-model的使用

1.关于数组的使用以及常见的函数 &#xff08;1&#xff09;在队尾操作函数&#xff1a;push&#xff08;&#xff09;&#xff1a;追加&#xff0c;pop&#xff08;&#xff09;&#xff1a;删除 arr.push(9,8,7,6); 这种批量追加的方式也是被允许的 &#xff08;2&#xf…

Gem5模拟器,关于Adding parameters to SimObjects and more events的一些问题记录(六)

目录 &#xff08;1&#xff09;为什么Gem是Python和C混合使用编程&#xff1f; &#xff08;2&#xff09;关于析构函数 创建类的时候一般都需要写上析构函数吗&#xff1f; &#xff08;3&#xff09;关于HelloObject和GoodbyeObject的先后后创建关系 &#xff08;1&…

C# 类 字段 方法

一 现实中的实物抽象为类 类(class)最基本的要素是 ① 字段(field):变量&#xff1b; ② 方法(method):函数&#xff1b; class Person {public string name;public int age;public void SayHello(){Console.WriteLine("Hello!My name is"name);}public string Ge…

(Python)第2章-12 输出三角形面积和周长 (15 分)

Python解决输入三条边输出面积与周长1.问题2.解决思路代码在孤单的日子里&#xff0c;你单枪匹马奋斗的样子酷毙了。江客&#xff1a;时荒![在这里插入图片描述](https://img-blog.csdnimg.cn/85fc4495dcfc4578a8612a432d8045cd.png#pic_center)1.问题 本题要求编写程序&#…

Arduino Uno零基础入门学习笔记——变量与函数

文章目录一、创建变量二、函数三、delay的例子总结一、创建变量 int currentTemperature;整数型变量 变量名字 分号 使用驼峰命名法对进行命名 有些程序员喜欢全部小写&#xff0c;有些程序员喜欢用下划线&#xff0c;所以如果要写一个my name的变量&#xff0c;他们常用的写…

vue3中的propemit

状态是什么&#xff1a; 页面中要显示的东西&#xff0c;放在一个变量中&#xff0c;每次更改完值&#xff0c;就会被拦截&#xff0c;同时再重新渲染页面&#xff1b; 状态的对立面就是属性&#xff1b; 可以没有状态&#xff0c;那只能用父组件传过来的属性来自己用&#…

Saga 模式

Saga 模式目录概述需求&#xff1a;设计思路实现思路分析1.2.适用场景&#xff1a;3.缺点&#xff1a;4.Saga的实现&#xff1a;参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,ma…