打印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<Map> queryList = new ArrayList<Map>();
        String rules = print.getFilterRules();
        if(StringUtils.isNotBlank(rules)){
            ObjectMapper mapper = new ObjectMapper();
            try {
                queryList = (List<Map>)mapper.readValue(rules, queryList.getClass());
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        PageInfo<ContractMaintenanceVo> contractPage = mContractMaintenanceService.listAllPage(vo, queryList);
        // 组装表头
        Map<String, String> 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<ContractMaintenanceVo> 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加载驱动的方式就是声明接口,然后将实现类让各个数据库的厂商进行实现,因此在遇到这种情况的时候也可以想起使用这种模式,声明一个接口,交给对应的实现类去实现,最后由统一方法进行数据整合输出即可!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,561评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,218评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,162评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,470评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,550评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,806评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,951评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,712评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,166评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,510评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,643评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,306评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,930评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,745评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,983评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,351评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,509评论 2 348

推荐阅读更多精彩内容