Java中使用反射对easyPoi做了一个统一导出接口(支持多sheet)

Posted vwvwvwgwg

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中使用反射对easyPoi做了一个统一导出接口(支持多sheet)相关的知识,希望对你有一定的参考价值。

需求

做excel导出----接口统一,写一个接口即可,避免过多冗余代码,和后期维护工作。

前言

最近项目好多excel导出的需求,真的太多了,所以一个一个写,就算是cv也太浪费了。然后就想了一下,是不是可以做一个统一导出的逻辑。然后用了半天的时间写了一个。因为有些知识忘记了,又复习了一下。

这里用到了 数据库和反射和easyPoi。当然,该方案是需要返回数据格式统一,这里使用Map格式。

那咱们废话不多说,开始!!!

开始实现

步骤一:数据库设计

首先我们既然要统一接口,那肯定是需要一些配置,因为每个导出用到的格式虽然一样,但是数据是不一样的,需要调用不同的service获取数据(我这里直接调用的Controller方法,因为根据我们这个项目逻辑,所以这么写的,Controller处理完成数据后,前端可以使用,然后导出接口也可以使用,两全其美),所以,所以,我们需要将配置找个地方存起来,可以选择配置文件,然后读取,可以使用数据库存储。我使用的是第二种方式,数据库。

因为有一些导出比较复杂,不一定是就导出一个表,可能是两个表,这里我的想法就是多sheet导出。

然后下面是我设计的数据库

-- 主表   一个模板对应多个sheet
CREATE TABLE `excel_configure` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT ‘导出的时候,只需要根据该id查询模板即可‘,
  `service_name` varchar(255) DEFAULT NULL COMMENT ‘Service名称,Service的路径,反射使用(我这里使用的是Controller)‘,
  `service_method_name` varchar(100) DEFAULT NULL COMMENT ‘Service方法名称,反射使用(Controller方法名)‘,
  `template_url` varchar(255) DEFAULT NULL COMMENT ‘模板地址‘,
  `file_name` varchar(100) DEFAULT NULL COMMENT ‘导出的文件名称‘,
  `save_path` varchar(255) DEFAULT NULL COMMENT ‘导出的路径‘,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC

-- 从表
CREATE TABLE `excel_configure_type` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `type_name` varchar(100) DEFAULT NULL COMMENT ‘返回的某个数据名称(相当于key)‘,
  `sheet_name` varchar(100) DEFAULT NULL COMMENT ‘该数据的sheet名称‘,
  `p_id` int(11) DEFAULT NULL COMMENT ‘模板id‘,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC

步骤二:接口编写

还是那句话,大家可以根据自己项目的业务逻辑来进行更改。这个逻辑只符合本项目的数据(如果你也是map数据,那是没有问题的。只需要吧接口什么的更改一下,缕清逻辑即可。不懂可以问,毕竟我已经把坑踩过了。)

Controller

    @Resource
    private ExcelConfigureService ExcelConfigureService;


    /**
     * 统一导出接口---多sheet
     *              单个sheet也是可以用的,但是必须符合下面的逻辑,返回数据必须是page列表或者是比例列表。
     * @param response 响应
     * @param params 前端传入的参数
     */
    @RequestMapping("/exportAllSheet")
    public void exportAllSheet(HttpServletResponse response, @RequestParam Map<String,String> params){
        //1.获取该模板数据-->根据传入参数获取数据库中模板配置-->后面根据模板配置通过反射获得对应的类和方法,进行获取导出数据
        ExcelConfigureEntity excelConfigureEntity = ExcelConfigureService.queryObject(Integer.parseInt(params.get("export")));
        //2.获取当前上下文环境,spring容器
        WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
        //2.1 加载class类
        Class<?> clazz = null;
        try {
            clazz = Class.forName(excelConfigureEntity.getServiceName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //2.2 获得方法
        Method m = null;
        try {
            m = clazz.getMethod(excelConfigureEntity.getServiceMethodName(), Map.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        //2.3 执行方法,获得数据
        R r = null;
        try {
            //2.3.1 切割被调用的路径,拿到最后一个类名,
            String s = excelConfigureEntity.getServiceName().split("\.")[excelConfigureEntity.getServiceName().split("\.").length - 1];
            //2.3.2 类名首字母转小写  -->首字母大写在ioc容器中找不到该类
            s = (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
            //2.3.3 执行
            r = (R) m.invoke(wac.getBean(s), params);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        //3. 填充数据
        Map<String, Object> map = new HashMap<String, Object>();
        //下面的参数根据自己的业务逻辑来,不需要直接删掉即可
        //3.1 开始时间-->根据前端条件获取-->固定参数
        String start = params.get("dateScopeStart");
        //3.2 结束时间-->根据前端条件获取-->固定参数
        String end = params.get("dateScopeEnd");
        //3.3 获取年-->根据前端条件获取-->固定参数
        String year = params.get("year");
        //3.4 获取月-->根据前端条件获取-->固定参数
        String month = params.get("month");
        //3.5 标题名称-->根据数据库配置读取
        map.put("title", excelConfigureEntity.getFileName()+(start!=null&&end!=null?start+"~"+end:"")+(year!=null?year+"年-"+(month!=null&&!month.equals("")?month+"月":"全年度"):""));
        //4.遍历查询每个sheet数据
        //4.1 定义遍历索引
        int i = 0;
        //4.2 定义需要填充的sheet名称数组-->长度是数据库读取到的集合长度
        String [] sheetArr = new String[excelConfigureEntity.getExcelConfigureTypeEntityList().size()];
        //4.3 遍历集合
        for (ExcelConfigureTypeEntity entity : excelConfigureEntity.getExcelConfigureTypeEntityList()) {
            //5.填充每个sheet列表数据
            //5.1 sheet名称
            sheetArr[i++] = entity.getSheetName();
            //5.2 判断是否为page,为page需要转换为PageUtils工具
            if ("page".equals(entity.getTypeName())) {
                //5.3 是page
                //5.3.1 根据类型获取到通过反射拿到的Map数据
                PageUtils pageUtils = (PageUtils) r.get(entity.getTypeName());
                //5.3.2 获取到数据列表
                List<Map<String,Object>> listMap = (List<Map<String, Object>>) pageUtils.getList();
                //5.3.3 将数据添加到需要导出的Map中--->就是excel导出数据--->名字是maplist+该类型名称(数据库中配置的)
                map.put("maplist"+entity.getTypeName(), listMap);
            }else {
                //5.4 不是page,说明需要计算比例数据
                //5.4.1 获取比例数据-->根据类型获取到通过反射拿到的Map数据
                Map<String,Object> projectChartData = (Map<String, Object>) r.get(entity.getTypeName());
                //5.4.2 将比例数据做处理,拿到处理好的数据Map
                Map<String, List<String>> proportion = new ProportionUtils().proportion(projectChartData);
                //5.4.3 定义临时list<Map>,用于接收遍历出的每一条比例
                List<Map<String,Object>> listMap = new ArrayList<>();
                //5.4.4 遍历比例数据,将比例数据处理成需要的格式
                for (Map.Entry<String, List<String>> entry : proportion.entrySet()) {
                    //5.4.4.1 定义Map,用于接收每次遍历出来的数据,放到临时List<Map>中--->放在循环外会导致每次数据都会更新成最后一次的数据,因为地址值原因
                    Map<String, Object> tempMap = new HashMap<>();
                    tempMap.put("name",entry.getKey());//比例名称
                    tempMap.put("count",entry.getValue().get(0));//比例数量
                    tempMap.put("proportion",entry.getValue().get(1));//比例%
                    //5.4.4.2 添加到临时Map中
                    listMap.add(tempMap);
                }
                //5.4.5 比例数据处理完毕,将数据添加到导出的Map中--->就是excel导出数据--->名字是maplist+该类型名称(数据库中配置的)
                map.put("maplist"+entity.getTypeName(),listMap);
            }
        }

        //6. 根据模板导出-->模板路径根据数据库配置读取,   true表示多sheet
        TemplateExportParams tempParams = new TemplateExportParams(excelConfigureEntity.getTemplateUrl(),true);
        //6.1 设置每个sheet名称-->数组格式-->在上面4.2已定义, 5.1处理数据时已经处理完毕,可直接使用
        tempParams.setSheetName(sheetArr);
        //6.2 调用工具easyPoi,进行导出 -->传入模板数据&需要导出的Map数据
        Workbook workbook = ExcelExportUtil.exportExcel(tempParams, map);

        /*6.3 保存地址-->暂时使用默认下载地址-->如需要指定下载地址,在数据库中配置后打开本段代码即可
            File savefile = new File(excelConfigureEntity.getSavePath());
            if (!savefile.exists()) {
                savefile.mkdirs();
            }
        */

        //7. 保存
        OutputStream output  = null;
        try {
            //7.1 通过响应获得输出流
            output = response.getOutputStream();
            //7.2 输出文件名称-->通过时间转换对象,将时间转换成指定格式
            String fileName = excelConfigureEntity.getFileName()+"-"+new SimpleDateFormat("yyyy-MM-dd").format(new Date());
            //7.3 添加响应头
            response.setHeader("Content-disposition", "attachment;filename="+new String(fileName.getBytes(), "ISO8859-1")+".xlsx");
            //7.4 响应格式
            response.setContentType("application/msexcel;charset=UTF-8");
            //7.5 输出
            workbook.write(output);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //7.6 执行完毕,关闭流,避免消耗内存
            try {
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

然后给大家分享一下测试接口-单sheet的

excel模板

技术图片

测试结果

技术图片

Java代码

虽然是测试,但是也是读取的真实数据

    /**
     * 测试接口---ok
     * @param response
     * @param params
     * @throws Exception
     */
    @RequestMapping("/testExport1")
    public void testExport(HttpServletResponse response, @RequestParam Map<String,String> params) throws Exception {
        //获取当前上下文环境,spring容器
        WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
        //加载class类
        Class<?> clazz = Class.forName("com.lcworld.service.SysUserService");
        //执行方法--读数据库
        Method m = clazz.getMethod("queryList", Map.class);
        //执行方法,获得数据
        Object invoke = m.invoke(wac.getBean("sysUserService"), params);
        List<Map<String, Object>> listMap  = (List<Map<String, Object>>) invoke;
        //加载模板
        TemplateExportParams tempParams = new TemplateExportParams("E:\test.xlsx");
        //填充数据
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("test", "2014-12-25");
        map.put("test1", 2000000.00);
        map.put("maplist", listMap);
        Workbook workbook = ExcelExportUtil.exportExcel(tempParams, map);
        //保存地址
        File savefile = new File("E:\");
        if (!savefile.exists()) {
            savefile.mkdirs();
        }
        //保存
        OutputStream output  = response.getOutputStream();
        Date now = new Date();
        //时间格式化
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
        //输出文件名称
        String fileName = "检测项目数量-"+format.format(now);
        //添加响应头
        response.setHeader("Content-disposition", "attachment;filename="+new String(fileName.getBytes(), "ISO8859-1")+".xlsx");
        //响应格式
        response.setContentType("application/msexcel;charset=UTF-8");
        workbook.write(output);
        //关闭流
        output.close();
    }

单sheet接口导出

前提还是每个需要导出的接口(这里是调用的service), 返回数据必须是统一的,统一map

数据库

-- 单sheet导出表
CREATE TABLE excel_configure(
  id INT PRIMARY KEY AUTO_INCREMENT,
  service_name VARCHAR(255) COMMENT ‘Service名称(类的路径)‘,
  service_method_name VARCHAR(20) COMMENT ‘Service方法名称‘,
  template_url VARCHAR(255) COMMENT ‘模板地址‘,
  file_name VARCHAR(20) COMMENT ‘导出文件名称‘,
  save_path VARCHAR(255) COMMENT ‘导出路径‘
)ENGINE = INNODB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

Controller

    /**
     * 统一导出入口---单个sheet
     * @param response
     * @param params
     */
    @RequestMapping("/exportAll")
    public void exportAll(HttpServletResponse response, @RequestParam Map<String,String> params){
        //获取该模板数据
        ExcelConfigureEntity excelConfigureEntity = ExcelConfigureService.queryObject(Integer.parseInt(params.get("export")));
        //获取当前上下文环境,spring容器
        WebApplicationContext wac = ContextLoader.getCurrentWebApplicationContext();
        //加载class类
        Class<?> clazz = null;
        try {
            clazz = Class.forName(excelConfigureEntity.getServiceName());
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //获得方法
        Method m = null;
        try {
            m = clazz.getMethod(excelConfigureEntity.getServiceMethodName(), Map.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        //执行方法,获得数据
        String[] arr = excelConfigureEntity.getServiceName().split("\.");
        List<Map<String, Object>> listMap  = null;
        try {
            //首字母转小写
            String s = arr[arr.length - 1];
            s = (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
            listMap = (List<Map<String, Object>>) m.invoke(wac.getBean(s), params);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

        //填充数据
        Map<String, Object> map = new HashMap<String, Object>();
        String start = params.get("dateScopeStart");//开始时间
        String end = params.get("dateScopeEnd");//结束时间

        map.put("title", excelConfigureEntity.getFileName()+(start!=null&&end!=null?start+"~"+end:""));
        map.put("maplist", listMap);
        //根据模板导出
        Workbook workbook = ExcelExportUtil.exportExcel(new TemplateExportParams(excelConfigureEntity.getTemplateUrl()), map);

        //保存地址--暂时使用默认地址
        /*
            File savefile = new File(excelConfigureEntity.getSavePath());
            if (!savefile.exists()) {
                savefile.mkdirs();
            }
        */

        //保存
        OutputStream output  = null;
        try {
            output = response.getOutputStream();
            Date now = new Date();
            //时间格式化
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
            //输出文件名称
            String fileName = excelConfigureEntity.getFileName()+"-"+format.format(now);
            //添加响应头
            response.setHeader("Content-disposition", "attachment;filename="+new String(fileName.getBytes(), "ISO8859-1")+".xlsx");
            //响应格式
            response.setContentType("application/msexcel;charset=UTF-8");
            //输出
            workbook.write(output);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭流
            try {
                output.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

OK,这篇就这样

写的有点笼统,因为项目接口数据是不一样的,所以,没有办法直接让你使用,可以试着理解一下,遇到问题问就可以。

 
来源:锌闻资讯

以上是关于Java中使用反射对easyPoi做了一个统一导出接口(支持多sheet)的主要内容,如果未能解决你的问题,请参考以下文章

SpringBoot加Poi仿照EasyPoi实现Excel导出

java EasyPoi导入/导出

Java实现PDF导出

EasyPoi的导入和导出功能

EasyPoi的导入和导出功能

使用EasyPoi导出Excel