作为一个后端仔,工作中不仅仅要面临着crud的折磨,更是会经常接到excel报表导出PDF生成等可以提升友军工作效率的需求。
但是Excel可比前端组件难用多了,特别是在遇到一些比较追求完美的友军的时候一些想法让人在不能拒绝的同时甚是难受。所以总结了两个比较烦的需求的解决方案:复杂表头和图片保持原大小
复杂表头实践
工作中表格是我们显示数据的最常用排版,无论是在前端还是excel都离不开表格结构。表格分为表头和内容两部分,当我们接到只有一行表头的excel导出需求时,此时我们的内心是这样的
但当我们被追问 "为什么前端小哥可以显示你后端就不能?"从而被要求导出这种格式的excel时
此时对于一个Java在我们的内心是可能是这样的
带着一丝迷茫 我们打开了百度/谷歌开始了漫长的搜索之旅 "Java导出Excel", "Java创建复杂表头"....
最终你会找到JXL、POI等操作excel的工具,然后找到合并单元格的api,然后变成"算法"工程师,展现你强大的计算能力。
计算出你需要多少个单元格以及每个单元格的跨度,然后开始merge单元格之旅,最终完成了设计。但是作为一个有追求(会偷懒)的后端仔,这种重复性的工作可是会要了我们老命,所以想出了一个粗浅的解决方案和大家分享下,给其他有同样困扰的职场新人一个小思路。哦,本文以JXL为例子进行试验
观察表头
从上面表头的格式来看,再结合我那easy水准的力扣水平,我们要想构造一个这种有父子、层级关系的表头,还是需要递归来帮我们解决。
@Datapublic class TableHeader { private String title; private int col; private int width; private int row; private int height; private int depth; private int colStart = 0; private int rowStart = 0; private int colEnd = 0; private int rowEnd = 0; private List children; public TableHeader() { reset(); } public TableHeader(String title) { reset(); this.title = title; } public void reset() { title = null; col = 0; width = 1; row = 0; height = 1; depth = 1; colStart = 0; rowStart = 0; colEnd = 0; rowEnd = 0; } public void addChildren(TableHeader header) { if (header == null) { return; } if (children == null) { children = Lists.newArrayList(); } children.add(header); } public TableHeader addSubTitle(String title) { if (StringUtils.isBlank(title)) { return null; } TableHeader subHeader = new TableHeader(title); addChildren(subHeader); return subHeader; }}
整个表头中的一个单元格 可以看成一个TableHeader对象,其子单元格也可以看做一个TableHeader对象。说了这些你可能会吐槽,别BB了,先上个例子。好那我们就上个实际使用的例子。
这就是我们最终要生成的excel表格的表头。如果要使用上面的数据结构,我们应该怎么表示呢?问的好:
只需要这几行代码就可以。表格的最上面的一个单元格为顶级单元格,其他的直接和其链接的都为其子单元格。如图中的部门、全部漏洞、有效漏洞都是部门漏洞统计的子单元格,所以直接使用addSubTitle方法添加即可,该方法会返回当前单元格,所以可以继续给子单元格添加子单元格,如图中的有效漏洞。从个人使用角度来讲,还是很方便的。
从上图中可以看到一些基本思路,构造表头单元格主要分为两步,即两个功能函数
1、调整坐标
在阅读了JXL的相关API后,我们可以了解到excel表格是没有浏览器那种流特性的。所以本质是还是要我们事先计算好每个单元格所占据的范围,然后进行从左上角坐标到右下角坐标的merge操作,来创建一个复杂的表头。TableHeader类中我们能看到这些基本的属性
(row, col) 就代表了一个单元格左上角的坐标,接下来就是对这个单元格的高度和宽度进行计算。
首先是计算整个表头中各个单元格的跨度,即宽和高知道了宽高才能设置左上角的坐标
宽度的设置很简单,就是子元素宽度之和
高度稍微有点绕,因为高度主要是同级元素的最高高度减去自己的子元素最高高度
至此我们已经调整好了表头中所有单元格的宽度和高度,然后就可以设置单元格起始坐标了和结束坐标了,思路很简单 ,根据自己的宽度和高度,进行简单的加减乘除计算就行。其中兄弟元素的col和子元素的row受到当前单元格的限制
2、创建真正的单元格
经过上面的操作,一个调整好坐标的表头对象已经构造好。我们直接拿来使用就行了。
由于其不具有流的特性,所以我们还是要分为两步 先根据坐标merge单元格 然后再往里面填充Label对象(单元格)
其中有个单元格列的宽度设置 是根据经验来的 并没有深入去了解他是如何换算的单位。
另外单元格的样式 我们不能用静态对象 这是一个BUG 否则会在现持久化的时候抛出IO异常 需要注意下 每次使用都应该返回一个新的对象
至此 我们的构造复杂表头的功能就完成了,最终我们还是将图片原模原样的插入到了excel中。
虽然作为服务端的我们都幻想着能够高并发大流量,但是难免会接到一些不那么让人舒服的需求。
但作为一名员工,这是我们不可推卸的职责,从小事做起吧~希望可以帮到同为职场新人的你。