打印table表格数据为excel文件的实践经验

采用JDBC的设计模式开发导出数据表格为excel表功能

1、需求分析

  • 提供统一的控制器Controller,统一的生成Excel表的Service方法。
  • 每个模块的开发者根据自身业务需求组装excel的表头和表身数据以Map(表头数据)和JSONArray组装的JSONObject(表身数据)

2、实现思想

  • 控制器接受各个模块的实现类、page、rows、sort等基本分页信息
  • service中使用反射Class.forName将模块实现类加载到JVM中,随后取出实现类的数据交给生成Excel方法进行excel表格文件生成。

3、实现代码以及步骤

  • 声明一个接受实现类名,page、row、sort等属性的实体类
public class PrintVo {

    @NotNull
    public String className;
    public Integer page;
    public Integer rows;
    public String sort;
    public String order;
    public String filterRules;
    // 节省篇幅不写getter/setter方法
}

  • 声明一个获取数据的接口
public interface GetDataForExcel {

    public ExcelDataVo getData(PrintVo print);
}
  • 用于获取对应的实现类的manager
public class GetDataForExcelManager {
    
    public static GetDataForExcel getDataForExcel = null;
    
    // 注册获取excel方法信息
    public static void registerGetDataForExcel(GetDataForExcel data) {
        getDataForExcel = data;
    }
    
    // 获取excel方法
    public static GetDataForExcel getGetDataForExcel() {
        return getDataForExcel;
    }

}
  • 控制器层接受参数并调用service
    public Result print(PrintVo printVo, HttpServletResponse response) {
        
        if (printVo.getClassName() == null) {
            return new Result(false, -1, "打印失败","打印参数有误","打印参数有误");
        }
        InputStream in = exportExcelService.generate(printVo);
        
        if (in == null) {
            return new Result(false, -1, "导出失败", "导出表格失败", "导出失败,请稍后重新尝试");
        }
        response.setContentType("application/vnd.ms-excel");
        try {
            response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode("表格导出.xlsx", "UTF-8"));
            FileCopyUtils.copy(in, response.getOutputStream());
        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 这句代码执行不到咯
        return new Result(false, -1, "导出成功", "导出表格成功", "导出成功");
    }
  • Service层使用Class.forName加载实现类到虚拟机中并获取数据执行生成excel操作
    public InputStream generate(PrintVo printVo) {
        
        try {
            // 1、反射调用对应的实现类
            Class.forName(printVo.getClassName());
            GetDataForExcel data = GetDataForExcelManager.getGetDataForExcel();
            // 2、传参到对应方法并获取表头表身信息
            ExcelDataVo excelData = data.getData(printVo);
            
            InputStream in = toExcel("POITest", "table export Excel", excelData.getHead(), excelData.getBody());
            return in;
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }
  • 以下是举例某一模块的代码,也就是不管有多少模块,都只需要遵循实现这个接口,并注册一个manager静态类即可
@Service
public class ContractMainPrintImpl implements GetDataForExcel{

    @Autowired
    private ContractMaintenanceService mContractMaintenanceService;
    
    @Override
    public ExcelDataVo getData(PrintVo print) {
        
        PageVo vo = new PageVo();
        vo.setPage(print.getPage());
        vo.setRows(print.getRows());
        vo.setSort(print.getSort());
        vo.setOrder(print.getOrder());
        
        // 这些代码是为了调用其它的service查询对应的数据
        List queryList = new ArrayList();
        String rules = print.getFilterRules();
        if(StringUtils.isNotBlank(rules)){
            ObjectMapper mapper = new ObjectMapper();
            try {
                queryList = (List)mapper.readValue(rules, queryList.getClass());
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        PageInfo contractPage = mContractMaintenanceService.listAllPage(vo, queryList);
        // 组装表头
        Map heads = new HashMap<>();
        heads.put("0", "表头1");
        heads.put("1", "表头2");
        heads.put("2", "表头3");
        heads.put("3", "表头4");
        heads.put("4", "表头5");
        // 组装表身
        JSONArray bodys = new JSONArray();
        List contractMaintenances = contractPage.getList();
        contractMaintenances.stream().forEach(c -> {
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("0", c.getInstitutionName());
            jsonObject.put("1", c.getAccountingCode());
            jsonObject.put("2", c.getInstitutionCode());
            jsonObject.put("3", c.getLicenseRegistrationCode());
            jsonObject.put("4", c.getLnstitutionalLevel());
            bodys.add(jsonObject);
        });
        
        ExcelDataVo excelData = new ExcelDataVo();
        excelData.setType("ContractMaintenanceVo");
        excelData.setBody(bodys);
        excelData.setFileName("合同签约主体信息表");
        excelData.setHead(heads);
        return excelData;
    }
}

// 在spring启动的时候会将对应实现类注入进来
@Component
public class ContractMainPrintManager {
    
    private static ContractMainPrintImpl mContractMainPrintImpl;
    
    @Autowired
    public void setContractMainPrintImpl(ContractMainPrintImpl contractMainPrintImpl) {
        ContractMainPrintManager.mContractMainPrintImpl = contractMainPrintImpl;
        GetDataForExcelManager.registerGetDataForExcel(mContractMainPrintImpl);
    }

}

  • 前台发送的请求只需要带上实现类的类名以及对应实现类需要的参数即可

  • tips

使用POI生成excel的方法在此暂不展示了,毕竟这个不属于这个话题中研究的问题,如有需要poi使用的可以在评论中留言。

小结

很早之前就知道JDBC加载驱动的方式就是声明接口,然后将实现类让各个数据库的厂商进行实现,因此在遇到这种情况的时候也可以想起使用这种模式,声明一个接口,交给对应的实现类去实现,最后由统一方法进行数据整合输出即可!

你可能感兴趣的:(打印table表格数据为excel文件的实践经验)