利用EasyExcel实现简易Excel导出

news2024/11/5 1:36:47

目标

通过注解形式完成对一个方法返回值的通用导出功能

工程搭建

pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.13</version>
    </parent>
    <groupId>com.example</groupId>
    <artifactId>export</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>export</name>
    <description>export</description>
    <properties>
        <java.version>1.8</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <spring-boot.version>2.7.6</spring-boot.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!--    EasyExcel    -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>4.0.3</version>
        </dependency>
        <!--    aspect    -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>
        <!--    util      -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.15.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>4.4</version>
        </dependency>

    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot.version}</version>
                <configuration>
                    <mainClass>com.example.export.ExportApplication</mainClass>
                    <skip>true</skip>
                </configuration>
                <executions>
                    <execution>
                        <id>repackage</id>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

核心代码

CusExport

package com.example.export.annotation;

import java.lang.annotation.*;

/**
 * @author PC
 * 基于EasyExcel实现动态列导出
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CusExport {
    /**
     * 导出数据对象
     */
    Class<?> dataClass() default Void.class;

    /**
     * 是否是动态的
     *
     * @return true 是 false 否
     */
    boolean dynamicFlag() default false;

    /**
     * 文件名,未指定取实体类名,无实体类取 getDefaultFileName()
     *
     * @return 文件名
     */
    String fileName() default "";
}

CusExportAspect

package com.example.export.aspect;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.example.export.annotation.CusExport;
import com.example.export.config.ExportProperties;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.*;

/**
 * @author PC
 * 导出切面
 */
@Aspect
@Component
public class CusExportAspect {
    private final static Logger logger = LoggerFactory.getLogger(CusExportAspect.class);

    private final ExportProperties exportProperties;

    @Autowired
    public CusExportAspect(ExportProperties exportProperties) {
        this.exportProperties = exportProperties;
    }

    @AfterReturning(pointcut = "@annotation(com.example.export.annotation.CusExport)", returning = "result")
    public void afterReturningAdvice(JoinPoint joinPoint, Object result) throws IOException {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        CusExport cusExport = methodSignature.getMethod().getAnnotation(CusExport.class);
        HttpServletResponse response = getHttpServletResponse();
        // 这里URLEncoder.encode可以防止中文乱码
        String generatorFileName = StringUtils.isEmpty(cusExport.fileName()) ? exportProperties.getDefaultFileName() : cusExport.fileName();
        String fileName = URLEncoder.encode(generatorFileName, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
        if (!cusExport.dynamicFlag()) {
            EasyExcel.write(response.getOutputStream(), cusExport.dataClass())
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet(exportProperties.getDefaultSheetName())
                    .doWrite((Collection<?>) result);
        } else {
            this.dealSpecified(cusExport, result, fileName, response);
        }
    }

    private static HttpServletResponse getHttpServletResponse() {
        ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attr != null;
        HttpServletResponse response = attr.getResponse();
        assert response != null;
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        return response;
    }

    @SuppressWarnings("unchecked")
    private void dealSpecified(CusExport cusExport, Object result, String fileName, HttpServletResponse response) throws IOException {
        if (Objects.isNull(cusExport) || Objects.isNull(result) || StringUtils.isEmpty(fileName)) {
            logger.error("the required field is missing");
        }
        logger.debug("the data type is not specified");
        List<Map<String, Object>> resultList = new ArrayList<>();
        if (result instanceof List) {
            resultList = (List<Map<String, Object>>) result;
        }
        if (CollectionUtils.isEmpty(resultList)) {
            logger.info("data is empty");
        }
        List<List<String>> headList = new ArrayList<>();
        boolean fillHeadFlag = false;
        for (Map<String, Object> resultItem : resultList) {
            if (!fillHeadFlag) {
                resultItem.keySet().forEach(keyCode -> headList.add(Collections.singletonList(keyCode)));
                fillHeadFlag = true;
            }
        }

        EasyExcel.write(response.getOutputStream())
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                // 放入动态头
                .head(headList).sheet(exportProperties.getDefaultSheetName())
                .doWrite(convertMapToList(resultList, headList));
    }

    private List<List<Object>> convertMapToList(List<Map<String, Object>> mapList, List<List<String>> headList) {
        List<List<Object>> list = new ArrayList<>();
        for (Map<String, Object> map : mapList) {
            List<Object> info = new ArrayList<>();
            for (List<String> itemHeadList : headList) {
                info.add(map.get(itemHeadList.get(0)));
            }
            list.add(info);
        }
        return list;
    }
}

测试

测试代码

在对应方法添加@CusExport注解即可,导出实体类需指定dataClass,导出动态数据需指定dynamicFlag = true,如需调整生成的文件名,还可以指定fileName

TestExportServiceImpl

package com.example.export.app.service.impl;

import com.example.export.annotation.CusExport;
import com.example.export.app.service.TestExportService;
import com.example.export.domain.entity.ExcelTest;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author PC
 */
@Component
public class TestExportServiceImpl implements TestExportService {
    @Override
    @CusExport(dataClass = ExcelTest.class)
    public List<ExcelTest> generatorDataForClass() {
        List<ExcelTest> excelTestList = new ArrayList<>();
        for (int j = 0; j < 100; j++) {
            ExcelTest excelTest = new ExcelTest();
            excelTest.setCode("testCode" + j);
            excelTest.setName("testName" + j);
            excelTest.setEnabledFlag(j % 2);
            excelTestList.add(excelTest);
        }
        return excelTestList;
    }

    @Override
    @CusExport(dynamicFlag = true)
    public List<Map<String, Object>> generatorDataForMap() {
        List<Map<String, Object>> excelTestList = new ArrayList<>();
        for (int j = 0; j < 100; j++) {
            Map<String, Object> item = new HashMap<>(3);
            item.put("code", "testCodeMap" + j);
            item.put("name", "testNameMap" + j);
            item.put("enabledFlag", j % 2);
            excelTestList.add(item);
        }
        return excelTestList;
    }
//
}

ExportTestController

package com.example.export.api.controller;

import com.example.export.app.service.TestExportService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author PC
 */
@RestController("v1.exportTestController")
@RequestMapping("/export")
public class ExportTestController {
    private final TestExportService testExportService;

    @Autowired
    public ExportTestController(TestExportService testExportService) {
        this.testExportService = testExportService;
    }

    @GetMapping("/by-class")
    public void exportTest(){
        testExportService.generatorDataForClass();
    }

    @GetMapping("/by-map")
    public void exportTestMap(){
        testExportService.generatorDataForMap();
    }
}

测试

访问127.0.0.1:18081/export/by-class

访问127.0.0.1:18081/export/by-map

参考资料

[1].EasyExcel官网文档

[2].demo

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

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

相关文章

基于java+SpringBoot+Vue的校园资料分享平台设计与实现

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven mysql5.7或8.0等等组成&#x…

VuePress文档初始化请求过多问题探讨

1. 背景 公司有部门使用VuePress 1.0搭建平台的帮助文档&#xff0c;前期文档不是很多&#xff0c;所以没有暴露出特别明显的问题。但随着文档的逐步迭代和内容的增多&#xff0c;出现了大量的并发请求&#xff0c;总共有218个请求&#xff0c;导致服务器带宽耗尽、响应速度下降…

入门 | Kafka数据使用vector消费到Loki中使用grafana展示

一、Loki的基本介绍 1、基本介绍 Loki 是由 Grafana Labs 开发的一款水平可扩展、高性价比的日志聚合系统。它的设计初衷是为了有效地处理和存储大量的日志数据&#xff0c;与 Grafana 生态系统紧密集成&#xff0c;方便用户在 Grafana 中对日志进行查询和可视化操作。 从架构…

C语言 运算符

时间&#xff1a;2024.11.4 一、学习内容 1、算数运算符&#xff08;加、减、乘、除法和取余&#xff09; 通用细节&#xff1a; 1.整数计算&#xff0c;结果一定是一个整数 2.小数计算&#xff0c;结果一定是一个小数 3.整数和小数计算&#xff0c;结果一定是一…

LLM | 论文精读 | AAAI | EarthVQA:向可查询地球迈进的多模态视觉问答研究

论文标题: EarthVQA: Towards Queryable Earth via Relational Reasoning-Based Remote Sensing Visual Question Answering 作者: Junjue Wang, Zhuo Zheng, Zihang Chen, Ailong Ma, Yanfei Zhong 期刊: The Thirty-Eighth AAAI Conference on Artificial Intelligence (AA…

总结拓展十五:SAP物料分割评估

一、概述 在SAP中&#xff0c;物料的分割评估是指&#xff0c;对同一个物料做不同的价值设置。即&#xff0c;根据不同的条件&#xff0c;核算其价格及记录价格的相关属性。 二、物料主数据设置 1、物料主数据-会计1视图 只有价格控制“V”可以分割评估。 2、设置评估类型…

ai翻唱部分步骤

模型部署 我是用的RVC进行的训练&#xff0c;也可以使用so-vits-svc。 通过百度网盘分享的文件&#xff1a;RVC-beta 链接&#xff1a;https://pan.baidu.com/s/1c99jR2fLChoqUFqf9gLUzg 提取码&#xff1a;4090 以Nvida显卡为例&#xff0c;分别下载“RVC1006Nvidia”和…

[综述笔记]Deep learning for brain disorder diagnosis based on fMRI images

论文网址&#xff1a;Deep learning for brain disorder diagnosis based on fMRI images - ScienceDirect 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向…

HarmonyOS:UIAbility组件概述

一、概述 UIAbility组件是一种包含UI的应用组件&#xff0c;主要用于和用户交互。 UIAbility的设计理念&#xff1a; 原生支持应用组件级的跨端迁移和多端协同。支持多设备和多窗口形态。 UIAbility划分原则与建议&#xff1a; UIAbility组件是系统调度的基本单元&#xff0c…

jeecgbootvue2菜单路由配置静态文件夹(public)下的html

需求:想要在菜单配置src/assets/iconfont/chart.html显示页面(目的是打包上线以后运维依然可以修改数据) 官网没有相关数据&#xff1a;菜单配置说明 JeecgBoot 开发文档 看云 问题现象: 我把文件放在src/assets/iconfont/chart.html然后在vue中作为 iframe 的 src 属性&am…

【Vue框架】基础语法练习(1)

其实更多知识点已经在Vue.js官网十分清楚了&#xff0c;大家也可以去官网进行更细节的学习 https://cn.vuejs.org/ 说明&#xff1a;目前最新是Vue3版本的&#xff0c;但是Vue2已经深得人心&#xff0c;所以就是可以支持二者合用。它们最大的区别就是Vue3是组合式API&#xf…

爬虫学习4

from threading import Thread#创建任务 def func(name):for i in range(100):print(name,i)if __name__ __main__:#创建线程t1 Thread(targetfunc,args("1"))t2 Thread(targetfunc, args("2"))t1.start()t2.start()print("我是诛仙剑")from …

qt QScrollArea详解

1、概述 QScrollArea是Qt框架中的一个控件&#xff0c;它提供了一个可滚动的视图区域&#xff0c;用于显示超出视图大小的内容。这个控件非常有用&#xff0c;尤其是在处理大型表格、文本区域、图像集合或任何需要滚动浏览的内容时。QScrollArea本身不直接显示内容&#xff0c…

【ChatGPT】搜索趋势分析

【ChatGPT】搜索趋势分析 为了分析 ChatGPT 在过去一年的流行趋势&#xff0c;我们可以查看 Google Trends 的数据 安装依赖pytrends pip install pytrends运行以下 Python 脚本 import pandas as pd import matplotlib.pyplot as plt from pytrends.request import TrendR…

Mac如何实现高效且干净的卸载应用程序

使用Mac卸载应用程序&#xff0c;你还在使用废纸篓这个办法吗&#xff0c;看不见卸载了什么&#xff0c;看不见清理了多少&#xff0c;真的不会有残留吗 XApp Mac上的卸载专家&#xff0c;强大的垃圾逻辑检测&#xff0c;垃圾扫描更全面&#xff0c;卸载更干净 使用简单&#…

安科瑞AMB400分布式光纤测温系统解决方案--远程监控、预警,预防电气火灾

安科瑞戴婷 可找我Acrel-Fanny 安科瑞AMB400电缆分布式光纤测温具有多方面的特点和优势&#xff1a; 工作原理&#xff1a; 基于拉曼散射效应。激光器产生大功率的光脉冲&#xff0c;光在光纤中传播时会产生散射。携带有温度信息的拉曼散射光返回光路耦合器&#xff0c;耦…

GitHub上传自己的项目

目录 一、安装Git插件 1&#xff09;下载 2&#xff09;安装 二、创建Gothub的创库 三、通过Git上传本地文件到Github 四、其他 1、部分指令 2、如果已经运行过git init并设置了[user]&#xff0c;下次可以直接用 一、安装Git插件 1&#xff09;下载 下载地址&#x…

Spring Boot框架

一.SpringBoot简介 1.1 设计初衷 目前我们开发的过程当中&#xff0c;一般采用一个单体应用的开发采用 SSM等框架进行开发&#xff0c;并在 开发的过程当中使用了大量的xml 等配置文件&#xff0c;以及在开发过程中使用MAVEN的构建工具来进 行构建项目&#xff0c;但是往往有…

【C++】vector 类深度解析:探索动态数组的奥秘

&#x1f31f;快来参与讨论&#x1f4ac;&#xff0c;点赞&#x1f44d;、收藏⭐、分享&#x1f4e4;&#xff0c;共创活力社区。 &#x1f31f; 如果你对string类还存在疑惑&#xff0c;欢迎阅读我之前的作品 &#xff1a; &#x1f449;【C】string 类深度解析&#xff1a;…

windows——病毒的编写

声明 学习视频来自B 站up主泷羽sec&#xff0c;如涉及侵权马上删除文章。 笔记的只是方便各位师傅学习知识&#xff0c;以下网站只涉及学习内容&#xff0c;其他的都与本人无关&#xff0c;切莫逾越法律红线&#xff0c;否则后果自负。 windows基础 我们学习了前面的window…