使用easyExcel灵活设置样式并导出数据到Excel


在这里插入图片描述

前言

之前使用poi实现了简单的数据填充及表格下载,由于时间花费太长,所以改为easyExcel进行处理。
并且本次修改的要求是,页面上选择好的数据,直接下载,去掉了跟数据库交互的代码。
链接: poi实现原文地址.
链接: easyExcel官方文档地址.
easyExcel 实测 3000条数据,花费 2.91秒,已经满足我的需要。


一、easyExcel的优势

相对来说,比poi更加简单入手,使用简单的几行代码就可以实现表格下载功能
例如(代码来源于官方文档)

    /**
     * 文件下载(失败了会返回一个有部分数据的Excel)
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link DownloadData}
     * <p>
     * 2. 设置返回的 参数
     * <p>
     * 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<Customer> 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);

效果展示
在这里插入图片描述

2.修改自定义拦截器,进行更灵活的内容设置

最后发现,无法针对某一列进行其他样式的设置,例如之前说的“表格宽度自适应,并且如果某一列数据量太大,需要固定该列的宽,并自动换行”。
经过翻阅资料,发现拦截器父类中的有一个方法很特殊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);
    }

最终效果展示
在这里插入图片描述

4.异常处理,不下载表格

如果发生异常,接口仍然返回200,表格也会下载成功,表格里边的数据就是异常信息的打印,这显然不符合需求。
所以进行异常捕捉,如果发生异常,响应流重置,自定义返回状态和信息。
然后前端就可以进行自定义的页面展示。
代码示例:

    @PostMapping("download")
    public void download(HttpServletResponse response, @RequestBody List<Customer> customerList)  {
        try {
           // 表格下载代码逻辑
        } catch (Exception e) {
            response.reset();
            response.setStatus(500);
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = new HashMap<String, String>();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

效果展示
在这里插入图片描述


遗留问题

  • easyExcel的列宽自适应,针对于中文效果并不好。

官方解释

 /**
     * 自动列宽(不太精确)
     * <p>
     * 这个目前不是很好用,比如有数字就会导致换行。而且长度也不是刚好和实际长度一致。 所以需要精确到刚好列宽的慎用。 当然也可以自己参照
     * {@link LongestMatchColumnWidthStyleStrategy}重新实现.
     * <p>
     * poi 自带{@link SXSSFSheet#autoSizeColumn(int)} 对中文支持也不太好。目前没找到很好的算法。 有的话可以推荐下。
     *
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link LongestMatchColumnWidthData}
     * <p>
     * 2. 注册策略{@link LongestMatchColumnWidthStyleStrategy}
     * <p>
     * 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());
    }
  • 有些特殊字符会被换行显示
    在这里插入图片描述

请求参数

[
	{
		"number": "1",
		"customerNo": "okvy5sy1qcdknCkby3WP9e-mwRHk",
		"customerName": "客户姓名",
		"customerDescription": "客户概述",
		"customerPhone": "12345678910",
		"consultantName": "咨询人姓名",
		"consultantPhone": "12345678910",
		"customerLevel": "客户等级",
		"agentName": "销售人员名字",
		"noTrace": "3",
		"lastTraceDate": "20169-06-25",
		"communicationRecord": "沟通记录沟通\n记录沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录\n沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录",
		"status": "未联系"
	},
	{
		"number": "1",
		"customerNo": "okvy5sy1qcdknCkby3WP9e-mwRHk",
		"customerName": "客户姓名",
		"customerDescription": "客户概述",
		"customerPhone": "12345678910",
		"consultantName": "咨询人姓名",
		"consultantPhone": "12345678910",
		"customerLevel": "客户等级",
		"agentName": "销售人员名字",
		"noTrace": "3",
		"lastTraceDate": "20169-06-25",
		"communicationRecord": "沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录沟通记录",
		"status": "未联系"
	}
]
  • 9
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
您好!对于使用EasyExcel导出Excel数据,您可以按照以下步骤进行操作: 1. 导入EasyExcel库:首先,您需要在项目中引入EasyExcel库的依赖。您可以在pom.xml(如果是Maven项目)或者build.gradle(如果是Gradle项目)文件中添加以下依赖: ``` <!-- Maven --> <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.4.3</version> </dependency> // Gradle implementation 'com.alibaba:easyexcel:2.4.3' ``` 2. 创建导出数据对象:根据您要导出数据结构,创建一个Java对象,并添加相应的注解,以便EasyExcel能够正确地将数据写入Excel文件中。 ```java public class ExportData { @ExcelProperty("姓名") private String name; @ExcelProperty("年龄") private int age; // Getters and setters } ``` 3. 执行导出操作:使用EasyExcel提供的API,将数据写入Excel文件。 ```java public void exportData(List<ExportData> dataList, String filePath) { // 设置写入Excel的参数 WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build(); try (ExcelWriter excelWriter = EasyExcel.write(filePath).build()) { // 将数据写入Excel文件 excelWriter.write(dataList, writeSheet); } } ``` 在上面的示例中,`dataList` 是您要导出数据列表,`filePath` 是导出Excel文件路径。 这就是使用EasyExcel导出Excel数据的基本步骤。您可以根据具体需求进行更多的配置和操作,例如设置表头样式、内容样式导出多个Sheet等。希望对您有所帮助!如果您有任何问题,请随时提问。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值