Spring Boot 我随手封装了一个万能的 Excel 导出工具,传什么都能导出!

简介: Spring Boot 我随手封装了一个万能的 Excel 导出工具,传什么都能导出!

前言

如题,这个小玩意,就是不限制你查的是哪张表,用的是什么类。


我直接一把梭,嘎嘎给你一顿导出。


我知道,这是很多人都想过的, 至少我就收到很多人问过我这个类似的问题。


我也跟他们说了,但是他们就是不动手,其实真的很简单。


不动手怎么办? 我出手呗。


不多说开搞 。


正文

玩法很简单。


要实现的效果 :


类是不确定的 ,User ?Student ? District ? 不确定。


但是呢我们封装出来的函数,要足够支撑不同的类,我们自动去读取遍历list ,然后导出生成文件。


核心的思路是什么 ?


其实就还是利用csv文件的内容格式本质 ,看这两幅图 :

8e0f3db6583ff679a33b1104b1a3035a_f8024466d1e147d09b82268360bbf7d1.png


8e55c1bd845c0eac2bfc3aab8bd31045_a96d2a9dfef747488c07b97ffaf55166.png



我们要实现万能的类导出excel !!!

思路是什么 :


① 我们从不确定的类 的集合list 中,取出 里面的类。

反射一手,拿出里面的属性名, 做第一行表格行标题名称拼接。


②拼接内容

因为类不确定,那么我们就采取反射把类全部字段属性作为key丢到map里面,同时把值丢到value里面。


这样我们拼接内容的时候只需要根据map 嘎嘎一顿遍历拼接即可。


推荐一个开源免费的 Spring Boot 最全教程:


https://github.com/javastacks/spring-boot-best-practice


1.依赖


  org.apache.poi

  poi-ooxml

  3.15

  org.apache.poi

  poi-scratchpad

  3.15

  com.alibaba

  fastjson

  1.2.69

  commons-io

  commons-io

  2.5


2. 核心的工具类,函数我都封装好了


MyCsvFileUtil.java


import lombok.extern.slf4j.Slf4j;

import org.apache.commons.io.IOUtils;


import java.io.File;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.OutputStreamWriter;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

import java.text.SimpleDateFormat;

import java.util.*;


/**

* @author JCccc

* @Remark 是我

*/

@Slf4j

public class MyCsvFileUtil {

   public static final String FILE_SUFFIX = ".csv";

   public static final String CSV_DELIMITER = ",";

   public static final String CSV_TAIL = "\r\n";

   protected static final String DATE_STR_FILE_NAME = "yyyyMMddHHmmssSSS";


   /**

    * 将字符串转成csv文件

    */

   public static void createCsvFile(String savePath, String contextStr) throws IOException {


       File file = new File(savePath);

       //创建文件

       file.createNewFile();

       //创建文件输出流

       FileOutputStream fileOutputStream = new FileOutputStream(file);

       //将指定字节写入此文件输出流

       fileOutputStream.write(contextStr.getBytes("gbk"));

       fileOutputStream.flush();

       fileOutputStream.close();

   }


   /**

    * 写文件

    *

    * @param fileName

    * @param content

    */

   public static void writeFile(String fileName, String content) {

       FileOutputStream fos = null;

       OutputStreamWriter writer = null;

       try {

           fos = new FileOutputStream(fileName, true);

           writer = new OutputStreamWriter(fos, "GBK");

           writer.write(content);

           writer.flush();

       } catch (Exception e) {

           log.error("写文件异常|{}", e);

       } finally {

           if (fos != null) {

               IOUtils.closeQuietly(fos);

           }

           if (writer != null) {

               IOUtils.closeQuietly(writer);

           }

       }

   }


   /**

    * 构建文件名称

    * @param dataList

    * @return

    */

   public static String buildCsvFileFileName(List dataList) {

       return dataList.get(0).getClass().getSimpleName() + new SimpleDateFormat(DATE_STR_FILE_NAME).format(new Date()) + FILE_SUFFIX;

   }


   /**

    * 构建excel 标题行名

    * @param dataList

    * @return

    */

   public static String buildCsvFileTableNames(List dataList) {

       Map map = toMap(dataList.get(0));

       StringBuilder tableNames = new StringBuilder();

       for (String key : map.keySet()) {

           tableNames.append(key).append(MyCsvFileUtil.CSV_DELIMITER);

       }

       return tableNames.append(MyCsvFileUtil.CSV_TAIL).toString();

   }


   /**

    * 构建excel内容

    * @param dataLists

    * @return

    */

   public static String buildCsvFileBodyMap(List dataLists) {

       //不管你传什么玩意,我都给你反射一手,搞成Map

       List> mapList = new ArrayList<>();

       for (Object o : dataLists) {

           mapList.add(toMap(o));

       }

       //然后利用csv格式,对着map嘎嘎一顿拼接数据

       StringBuilder lineBuilder = new StringBuilder();

       for (Map rowData : mapList) {

           for (String key : rowData.keySet()) {

               Object value = rowData.get(key);

               if (Objects.nonNull(value)) {

                   lineBuilder.append(value).append(MyCsvFileUtil.CSV_DELIMITER);

               } else {

                   lineBuilder.append("--").append(MyCsvFileUtil.CSV_DELIMITER);

               }

           }

           lineBuilder.append(MyCsvFileUtil.CSV_TAIL);

       }

       return lineBuilder.toString();

   }


   /**

    * 类转map

    * @param entity

    * @param

    * @return

    */

   public static Map toMap(T entity){

       Class bean = entity.getClass();

       Field[] fields = bean.getDeclaredFields();

       Map map = new HashMap<>(fields.length);

       for(Field field:fields){

           try {

               if(!"serialVersionUID".equals(field.getName())){

                   String methodName = "get"+field.getName().substring(0, 1).toUpperCase()+field.getName().substring(1);

                   Method method = bean.getDeclaredMethod(methodName);

                   Object fieldValue = method.invoke(entity);

                   map.put(field.getName(),fieldValue);

               }

           } catch (Exception e) {

               log.warn("toMap() Exception={}",e.getMessage());

           }

       }

       return map;

   }

}


代码注意点(各种小封装):


①类转map


54ba1fbadc7ca96b3fb8597331b9c363_c95c7a40addb44d08f4b7d7bbec933c3.png


② 反射转map 取字段属性名 拼接 标题


c56d86312607cbd9308b15b082195e8e_5364dcc697364fd889054f6387c50c99.png


③ 针对list<不确定类>转化成list,然后拼接excel内容


4cb63f68e54a28f9ffd1fb53fdee2464_29bdbaccdbc94e839a93e2bd579eb2d6.png


测试代码:


@RequestMapping("/createCsvFileJcTest")

public void createCsvFileJcTest() {

   //类不确定 随便怎么传都行

   List districts = districtService.queryByParentCodes(Arrays.asList("110100"));

   //存放地址&文件名

   String fileName = "D:\\mycsv\\"+MyCsvFileUtil.buildCsvFileFileName(districts);

   //创建表格行标题

   String tableNames = MyCsvFileUtil.buildCsvFileTableNames(districts);

   //创建文件

   MyCsvFileUtil.writeFile(fileName, tableNames);

   //写入数据

   String contentBody = MyCsvFileUtil.buildCsvFileBodyMap(districts);

   //调用方法生成

   MyCsvFileUtil.writeFile(fileName, contentBody);

}

72f946a87189b5404715f6dd6ae1e35d_c56f0ad9b1f944f0bbf9afc95d979e16.png


看看效果:

1ceed3d9b54937c701b466bebc342d41_929811d21e3e4cb3a84a5210b8451270.png


5adcf14fc4d273a083fa56be54f27b73_0e38f781c6a34a4da89d4c3b49c89fab.png



导出的excel文件内容:


4d91c9e54111cb6c102dd852e332342e_4da70e72b9fd423b8904564e9145a16a.png


接下来换个类玩玩:


5ccf378c8ac8427c98e37a42ffcf994d_e0c9cefe767849fea5a96a5e904a8713.png


然后导出看看效果:


2c2e93bd2e2e857b7bf8ea259d3d87f8_5e161c29b7bf4836a5206d28777c18bb.png


可以看到数据导出也是OK的:


22d2a101ffec60074876072c7f11e80a_4b1fb15e5c264d44809b7a24a620c218.png


没错就是这么简单, 当然也是抛转引玉, 希望大家看了这篇文章,可以借鉴这些反射的函数玩法,做更多的好玩的封装,比如加上一些自定义注解的解析,比如加上一些前后置拦截器拓展等等。


扩展

上面的示例中导出的表头是属性名,如果正式的导出通常需要自定义表头名称,我们这里可以使用自定义注解来完成。


JcExcelName.java


/**

* @Author : JCccc

* @CreateTime : 2020/5/14

* @Description :

**/


@Target({ElementType.METHOD, ElementType.FIELD})

@Retention(RetentionPolicy.RUNTIME)

public @interface JcExcelName {


   String name() default "";


然后在想导出的类里面,想加看得懂的名字就加,不加就拿属性名:

89327bae0ee23b3972d4bb6a0b5193bb_3f994bfdf74742c08e16071d8a7a472c.png



随手再写一个 ,新的反射解析拿字段属性注解值函数:


public static  List resolveExcelTableName(T entity) {

   List tableNamesList = new ArrayList<>();

   Class bean = entity.getClass();

   Field[] fields = bean.getDeclaredFields();

   Map map = new HashMap<>(fields.length);

   for (Field field : fields) {

       try {

           if (!"serialVersionUID".equals(field.getName())) {

               String tableTitleName = field.getName();

               JcExcelName myFieldAnn = field.getAnnotation(JcExcelName.class);

               String annName = myFieldAnn.name();

               if (StringUtils.hasLength(annName)) {

                   tableTitleName = annName;

               }

               tableNamesList.add(tableTitleName);

           }

       } catch (Exception e) {

           log.warn("toMap() Exception={}", e.getMessage());

       }

   }

   return tableNamesList;

}



然后根据解析出来的注解值列名拼接 表格标题名格式:


public static String buildCsvFileTableNamesNew(List dataList) {

   StringBuilder tableNames = new StringBuilder();

   for (String name : dataList) {

       tableNames.append(name).append(MyCsvFileUtil.CSV_DELIMITER);

   }

   return tableNames.append(MyCsvFileUtil.CSV_TAIL).toString();

}


测试看看效果:


public static void main(String[] args) {


   User user = new User();

   List nameList = MapUtils.resolveExcelTableName(user);

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

   String tableNames = buildCsvFileTableNamesNew(nameList);

   System.out.println(tableNames);


}


效果嘎嘎好:


71c397e2ab3ced13d4be7637dc72232d_2239ae1ee8a44a87a7a91fd85b5b4b6d.png


然后反手就搞到我们前面的文章使用例子里面:


String tableNames = MyCsvFileUtil.buildCsvFileTableNamesNew( MyCsvFileUtil.resolveExcelTableName(dataList.get(0)));

4d7e530eadd7de1a6b6874863e0e7486_e9b46a37922b49bca7e634df19e8b5e3.png


执行一下示例接口,看看效果:


e840b38fe45343cc7a9443185bf4a5e2_99129a30b608400ba3ce74130748991a.png


文件出来了:

8432ef38a89b7bf799e92f4bfbf5b331_5b37ec3ddbb346bc84e1ed72bbbb6a56.png



打开看看效果:


5083eafeaa4ea170dfb860a0f8ab113b_f03eb391522846b8b6aa47100c93c4e0.png


好了,就到这吧,非常完美。


来源:blog.csdn.net/qq_35387940/article/details/129062470

————————————————

版权声明:本文为CSDN博主「Java技术栈」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/youanyyou/article/details/130846002

相关文章
|
SQL 分布式计算 数据挖掘
从Excel到高级工具:数据分析进阶指南
从Excel到高级工具:数据分析进阶指南
538 54
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot返回Json数据及数据封装——封装统一返回的数据结构
本文介绍了在Spring Boot中封装统一返回的数据结构的方法。通过定义一个泛型类`JsonResult&lt;T&gt;`,包含数据、状态码和提示信息三个属性,满足不同场景下的JSON返回需求。例如,无数据返回时可设置默认状态码&quot;0&quot;和消息&quot;操作成功!&quot;,有数据返回时也可自定义状态码和消息。同时,文章展示了如何在Controller中使用该结构,通过具体示例(如用户信息、列表和Map)说明其灵活性与便捷性。最后总结了Spring Boot中JSON数据返回的配置与实际项目中的应用技巧。
978 0
|
10月前
|
JSON Java 数据格式
Spring Boot返回Json数据及数据封装
在Spring Boot中,接口间及前后端的数据传输通常使用JSON格式。通过@RestController注解,可轻松实现Controller返回JSON数据。该注解是Spring Boot新增的组合注解,结合了@Controller和@ResponseBody的功能,默认将返回值转换为JSON格式。Spring Boot底层默认采用Jackson作为JSON解析框架,并通过spring-boot-starter-json依赖集成了相关库,包括jackson-databind、jackson-datatype-jdk8等常用模块,简化了开发者对依赖的手动管理。
834 3
|
分布式计算 大数据 数据处理
从Excel到大数据:别让工具限制你的思维!
从Excel到大数据:别让工具限制你的思维!
598 85
|
人工智能 自然语言处理 Java
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
FastExcel 是一款基于 Java 的高性能 Excel 处理工具,专注于优化大规模数据处理,提供简洁易用的 API 和流式操作能力,支持从 EasyExcel 无缝迁移。
3773 65
FastExcel:开源的 JAVA 解析 Excel 工具,集成 AI 通过自然语言处理 Excel 文件,完全兼容 EasyExcel
|
12月前
|
人工智能 算法 安全
使用CodeBuddy实现批量转换PPT、Excel、Word为PDF文件工具
通过 CodeBuddy 实现本地批量转换工具,让复杂的文档处理需求转化为 “需求描述→代码生成→一键运行” 的极简流程,真正实现 “技术为效率服务” 的目标。感兴趣的快来体验下把
807 10
|
人工智能 数据可视化 前端开发
Probly:开源 AI Excel表格工具,交互式生成数据分析结果与可视化图表
Probly 是一款结合电子表格功能与 Python 数据分析能力的 AI 工具,支持在浏览器中运行 Python 代码,提供交互式电子表格、数据可视化和智能分析建议,适合需要强大数据分析功能又希望操作简便的用户。
1716 2
|
人工智能 自然语言处理 JavaScript
Univer:开源全栈 AI 办公工具,支持 Word、Excel、PPT 等文档处理和多人实时协作
Univer 是一款开源的 AI 办公工具,支持 Word、Excel 等文档处理的全栈解决方案。它具有强大的功能、高度的可扩展性和跨平台兼容性,适用于个人和企业用户,能够显著提高工作效率。
3146 9
Univer:开源全栈 AI 办公工具,支持 Word、Excel、PPT 等文档处理和多人实时协作
|
Java Maven Spring
【Spring工具插件】lombok使用和EditStarter插件
本文第一个板块主要介绍了SpringMVC中lombok依赖的引入,和相应的使用方法,以及浅显的原理解释,第二个板块主要介绍EditStarter插件的安装与使用
234 13
|
7月前
|
JavaScript Java 关系型数据库
基于springboot的项目管理系统
本文探讨项目管理系统在现代企业中的应用与实现,分析其研究背景、意义及现状,阐述基于SSM、Java、MySQL和Vue等技术构建系统的关键方法,展现其在提升管理效率、协同水平与风险管控方面的价值。

热门文章

最新文章