使用easyExcel灵活设置样式并导出数据到Excel |
您所在的位置:网站首页 › excel中样式不包括对齐吗 › 使用easyExcel灵活设置样式并导出数据到Excel |
文章目录
前言一、easyExcel的优势二、开始修改1.访问方式改为post,接收页面参数2.模仿官方实例,实现下载3.自定义样式
三、样式修改源码1.自定义样式,进行表头和内容的样式设置2.修改自定义拦截器,进行更灵活的内容设置3、列宽固定设置4.异常处理,不下载表格
遗留问题请求参数
之前使用poi实现了简单的数据填充及表格下载,由于时间花费太长,所以改为easyExcel进行处理。 并且本次修改的要求是,页面上选择好的数据,直接下载,去掉了跟数据库交互的代码。 链接: poi实现原文地址. 链接: easyExcel官方文档地址. easyExcel 实测 3000条数据,花费 2.91秒,已经满足我的需要。 一、easyExcel的优势相对来说,比poi更加简单入手,使用简单的几行代码就可以实现表格下载功能 例如(代码来源于官方文档) /** * 文件下载(失败了会返回一个有部分数据的Excel) ** 1. 创建excel对应的实体对象 参照{@link DownloadData} * * 2. 设置返回的 参数 * * 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大 */ @GetMapping("download") public void download(HttpServletResponse response) throws IOException { // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.setCharacterEncoding("utf-8"); // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系 String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data()); } 可以看到,跟poi相比省去了很多代码,其中的data()方法,就是需要我们自己补充的数据查询的业务逻辑。 二、开始修改 1.访问方式改为post,接收页面参数 @PostMapping("download") public void download(HttpServletResponse response, @RequestBody List customerList)其中的Customer,就是页面上的数据对应的实体类对象 2.模仿官方实例,实现下载总体的结果很明显,从之前的6秒,减少到600毫秒,但是问题也出现了。 3.自定义样式根据官方的例子,下载后的表格样式几乎没有,所以如果有样式的需求,则需要自己动手修改样式。 样式要求如下: 表头:背景颜色:天蓝色,字体:宋体、居中、大小14、加粗, 内容:无背景色,字体:宋体,居中,大小12 表格宽度自适应,并且如果某一列数据量太大,需要固定该列的宽,并自动换行 三、样式修改 源码gitee 地址 1.自定义样式,进行表头和内容的样式设置 自定义拦截器,继承 HorizontalCellStyleStrategy,重新构造方法。 public class CustomHandler extends HorizontalCellStyleStrategy { private static final String RECORD = "Record"; public CustomHandler(WriteCellStyle headWriteCellStyle, WriteCellStyle contentWriteCellStyle) { super(headWriteCellStyle, contentWriteCellStyle); } } 自定义表头和内容的样式 public static WriteCellStyle getHeadStyle() { // 头的样式 WriteCellStyle headWriteCellStyle = new WriteCellStyle(); // 下边是具体实现 } public static WriteCellStyle getContentStyle() { // 内容的样式 WriteCellStyle contentWriteCellStyle = new WriteCellStyle(); // 下边是具体实现 } 使用自定义的样式进行构建拦截器,最后输出表格的时候,使用registerWriteHandler,将自定义的拦截器注册上去。 CustomHandler customHandler = new CustomHandler(headWriteCellStyle, contentWriteCellStyle); write(response.getOutputStream(), Customer.class) .registerWriteHandler(customHandler) .sheet("信息").doWrite(customerList); 使用LongestMatchColumnWidthStyleStrategy 进行列宽自适应设置,同样进行注册 write(response.getOutputStream(), Customer.class) .registerWriteHandler(new LongestMatchColumnWidthStyleStrate .registerWriteHandler(customHandler) .sheet("信息").doWrite(customerList);效果展示 最后发现,无法针对某一列进行其他样式的设置,例如之前说的“表格宽度自适应,并且如果某一列数据量太大,需要固定该列的宽,并自动换行”。 经过翻阅资料,发现拦截器父类中的有一个方法很特殊setContentCellStyle 。 该方法的入参有个: Head head,如果能从head中拿到列名,那么就可以针对某一列的内容进行其他的样式设置。 于是重写setContentCellStyle,其中head.getFieldName()可以拿到列名。 @Override protected void setContentCellStyle(Cell cell, Head head, Integer relativeRowIndex) { if (head.getFieldName().endsWith(RECORD)) { // 如果该列的名字以 Record 结尾,则进行单独的样式设置 Workbook wb = cell.getSheet().getWorkbook(); CellStyle cellStyle = wb.createCellStyle(); // 左对齐,垂直居中对齐 cellStyle.setAlignment(HorizontalAlignment.LEFT); cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); // 边框线 cellStyle.setBorderLeft(BorderStyle.THIN); cellStyle.setBorderRight(BorderStyle.THIN); cellStyle.setBorderBottom(BorderStyle.THIN); cellStyle.setBorderTop(BorderStyle.THIN); // 自动换行 cellStyle.setWrapText(true); cell.setCellStyle(cellStyle); } else { super.setContentCellStyle(cell, head, relativeRowIndex); } } 3、列宽固定设置由于列宽的设置属于 sheet 层次,之前设置的都是 cell 单元格层次。因此继续查找父类拦截器的方法。 发现 beforeCellCreate,该方法的入参有:writeSheetHolder。所以如果能获得 sheet对象,那么就能对列宽设置。 重写 beforeCellCreate @Override public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) { Sheet sheet = writeSheetHolder.getSheet(); // 设置固定列宽,在第 11+1=12 列(从0开始)设置宽度固定 sheet.setColumnWidth(11, 40 * 256); super.beforeCellCreate(writeSheetHolder, writeTableHolder, row, head, columnIndex, relativeRowIndex, isHead); }最终效果展示 如果发生异常,接口仍然返回200,表格也会下载成功,表格里边的数据就是异常信息的打印,这显然不符合需求。 所以进行异常捕捉,如果发生异常,响应流重置,自定义返回状态和信息。 然后前端就可以进行自定义的页面展示。 代码示例: @PostMapping("download") public void download(HttpServletResponse response, @RequestBody List customerList) { try { // 表格下载代码逻辑 } catch (Exception e) { response.reset(); response.setStatus(500); response.setContentType("application/json"); response.setCharacterEncoding("utf-8"); Map map = new HashMap(); map.put("status", "failure"); map.put("message", "下载文件失败" + e.getMessage()); response.getWriter().println(JSON.toJSONString(map)); } }效果展示 官方解释 /** * 自动列宽(不太精确) ** 这个目前不是很好用,比如有数字就会导致换行。而且长度也不是刚好和实际长度一致。 所以需要精确到刚好列宽的慎用。 当然也可以自己参照 * {@link LongestMatchColumnWidthStyleStrategy}重新实现. * * poi 自带{@link SXSSFSheet#autoSizeColumn(int)} 对中文支持也不太好。目前没找到很好的算法。 有的话可以推荐下。 * * * 1. 创建excel对应的实体对象 参照{@link LongestMatchColumnWidthData} * * 2. 注册策略{@link LongestMatchColumnWidthStyleStrategy} *
* 3. 直接写即可
*/
@Test
public void longestMatchColumnWidthWrite() {
String fileName =
TestFileUtil.getPath() + "longestMatchColumnWidthWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, LongestMatchColumnWidthData.class)
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("模板").doWrite(dataLong());
}
有些特殊字符会被换行显示 |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |