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

相关文章
|
1月前
|
人工智能 Python
读取excel工具:openpyxl | AI应用开发
`openpyxl` 是一个 Python 库,专门用于读写 Excel 2010 xlsx/xlsm/xltx/xltm 文件。它是处理 Excel 文件的强大工具,可以让你在不需要安装 Excel 软件的情况下,对 Excel 文件进行创建、修改、读取和写入操作【10月更文挑战第3天】
61 0
|
6天前
|
SQL 数据可视化 数据挖掘
想让Excel表格设计更美观?试试这几款好用工具!
Excel表格设计在项目管理和数据分析中至关重要。本文推荐四款辅助工具:板栗看板、Excel自动图表助手、Think-Cell Chart 和 Power BI,分别在任务管理、图表生成、数据可视化等方面表现突出,帮助你设计出更专业、美观的表格。
20 2
|
14天前
|
Java
springboot将list封装成csv文件
springboot将list封装成csv文件
19 4
|
13天前
|
存储 easyexcel Java
SpringBoot+EasyExcel轻松实现300万数据快速导出!
本文介绍了在项目开发中使用Apache POI进行数据导入导出的常见问题及解决方案。首先比较了HSSFWorkbook、XSSFWorkbook和SXSSFWorkbook三种传统POI版本的优缺点,然后根据数据量大小推荐了合适的使用场景。接着重点介绍了如何使用EasyExcel处理超百万数据的导入导出,包括分批查询、分批写入Excel、分批插入数据库等技术细节。通过测试,300万数据的导出用时约2分15秒,导入用时约91秒,展示了高效的数据处理能力。最后总结了公司现有做法的不足,并提出了改进方向。
|
18天前
|
数据处理
在Excel中,通配符是一种强大的工具
【10月更文挑战第23天】在Excel中,通配符是一种强大的工具
15 4
|
1月前
|
easyexcel Java UED
SpringBoot中大量数据导出方案:使用EasyExcel并行导出多个excel文件并压缩zip后下载
在SpringBoot环境中,为了优化大量数据的Excel导出体验,可采用异步方式处理。具体做法是将数据拆分后利用`CompletableFuture`与`ThreadPoolTaskExecutor`并行导出,并使用EasyExcel生成多个Excel文件,最终将其压缩成ZIP文件供下载。此方案提升了导出效率,改善了用户体验。代码示例展示了如何实现这一过程,包括多线程处理、模板导出及资源清理等关键步骤。
|
1月前
|
Java BI API
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
这篇文章介绍了如何在Spring Boot项目中整合iTextPDF库来导出PDF文件,包括写入大文本和HTML代码,并分析了几种常用的Java PDF导出工具。
374 0
spring boot 整合 itextpdf 导出 PDF,写入大文本,写入HTML代码,分析当下导出PDF的几个工具
|
1月前
|
Java API Spring
springBoot:注解&封装类&异常类&登录实现类 (八)
本文介绍了Spring Boot项目中的一些关键代码片段,包括使用`@PathVariable`绑定路径参数、创建封装类Result和异常处理类GlobalException、定义常量接口Constants、自定义异常ServiceException以及实现用户登录功能。通过这些代码,展示了如何构建RESTful API,处理请求参数,统一返回结果格式,以及全局异常处理等核心功能。
|
1月前
|
监控 Java 对象存储
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
监控与追踪:如何利用Spring Cloud Sleuth和Netflix OSS工具进行微服务调试
42 1
|
2月前
|
Java 应用服务中间件 Spring
IDEA 工具 启动 spring boot 的 main 方法报错。已解决
IDEA 工具 启动 spring boot 的 main 方法报错。已解决