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

相关文章
|
22天前
|
easyexcel
【EasyExcel】第二篇:导出excel文件,导出多个sheet工作空间
【EasyExcel】第二篇:导出excel文件,导出多个sheet工作空间
|
29天前
|
SQL 数据库连接 数据库
【SQL Server】2. 将数据导入导出到Excel表格当中
【SQL Server】2. 将数据导入导出到Excel表格当中
43 0
|
23天前
|
JavaScript 前端开发
【导出Excel】Vue实现导出下载Excel文件(blob文件流)--亲测可用
【导出Excel】Vue实现导出下载Excel文件(blob文件流)--亲测可用
【导出Excel】Vue实现导出下载Excel文件(blob文件流)--亲测可用
|
23天前
|
开发框架 安全 Java
探索 Spring 框架:企业级应用开发的强大工具
探索 Spring 框架:企业级应用开发的强大工具
31 2
|
3天前
|
IDE Java 开发工具
Spring Boot DevTools:加速开发的热部署工具
【4月更文挑战第28天】在Spring Boot的开发过程中,快速反馈和效率至关重要。Spring Boot DevTools是一个为开发者设计的模块,支持热部署(hot swapping),能够实现应用的快速重启和自动重载,极大地提高了开发效率。本篇博客将介绍Spring Boot DevTools的核心概念,并通过具体的实战示例展示如何在开发过程中利用这一工具。
12 0
|
12天前
|
easyexcel 数据库
公司大佬对excel导入、导出的封装,那叫一个秒啊
封装公司统一使用的组件的主要目标是为了简化开发人员的调用流程,避免各个项目组重复集成和编写不规范的代码。文中提到对阿里EasyExcel进行了二次封装,提供了导入和导出功能,并支持模板的导入和导出。此外,还处理了读取数据与实际保存数据不一致的情况,通过提供自定义转换器来解决。
36 0
|
12天前
|
数据库
开发指南009-从list导出excel文件
从数据库返回一般是对象的列表,平台底层提供了从list转为excel文件的方法
|
12天前
|
前端开发
开发指南007-导出Excel
平台上开发导出Excel比过去的单体架构要复杂些,因为前端和后台不在一个进程空间里。
|
23天前
|
C#
C# gridControl 导出Excel
C# gridControl 导出Excel
|
28天前
|
开发框架 安全 Java
探索 Spring 框架:企业级应用开发的强大工具
探索 Spring 框架:企业级应用开发的强大工具
20 1