EasyExcel复杂表头导出(一对多)

一、前言

之前,写了 EasyExcel复杂表头导入(一对多)的博客,效果不错,好多网友留言让我再写一个导入的。盛情难却,就写了一个,发现问题很多。

现已通过自定义拦截器的形式完美解决了该博客的末尾的遗留问题,地址如下:

https://blog.csdn.net/qq_41057885/article/details/126411957

关于EasyPoi 框架的问题,在此不再赘述,参考我的另一篇博客,地址如下:

EasyExcel复杂表头导入(一对多)_间歇性悲伤患者的博客-CSDN博客_easyexcel复杂表头

实际上,官方文档和网上都没有详细的导出教程,需要自己参考官方去实现。我搞了半天只弄成功了一个半桶水的,十分惭愧,在此记录一下,方便回溯。

说明:EasyExcel无法处理List里面嵌套List的情况,我采用的方案是通过表格合并的来实现。

下面,先给出具体实现,然后再谈谈问题。

二、EasyExcel一对多导出的实现

2.1 Entity 对象

/**
 * 客户信息导出类,指明导出模板样式等。是真正和EasyPoi交互的类
 */
@Data
@EqualsAndHashCode
@HeadRowHeight(30)
@ContentRowHeight(20)
@ColumnWidth(20)
@HeadStyle(fillForegroundColor = 44)
@NoArgsConstructor
class Customer extends BaseRowModel implements Serializable {

    @ExcelProperty({"客户编号"})
    private String userCode;

    @ExcelProperty({"客户名称"})
    private String userName;

    @ColumnWidth(25)
    @ExcelProperty({"客户所在地址"})
    private String address;

    @ExcelProperty({"联系人信息", "联系人姓名"})
    private String personName;

    @ExcelProperty({"联系人信息", "联系电话"})
    private String telephone;

    public Customer(CustomerInfo customerInfo) {
        this.userCode = customerInfo.getUserCode();
        this.userName = customerInfo.getUserName();
        this.address = customerInfo.getAddress();
        this.personName = customerInfo.getPersonList().get(0).getPersonName();
        this.telephone = customerInfo.getPersonList().get(0).getTelephone();
    }

    public Customer(CustomerInfo.Person person) {
        this.personName = person.getPersonName();
        this.telephone = person.getTelephone();
    }

}

/**
 * 客户基本信息类,类比程序从service层拿到的信息
 * 实际上,EasyPoi只能读取简单String、boolean、integer、float byte[] 等简单数据类型。无法处理List、Map等数据类型。
 * 当前,你也可以只定义类型转换器Converter,具体见文章末尾的参考连接。
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
class CustomerInfo {

    private String userCode;

    private String userName;

    private String address;

    private List<Person> personList;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Person {
        private String personName;

        private String telephone;
    }

}

2.2 Controller 层

@IgnoreUserToken
@GetMapping("/exportExcel")
@ApiOperation("导出Excel")
public void exportExcel(HttpServletResponse response) throws Exception {
    // 获取导出数据,模拟从service层拿到list数据。
    List<CustomerInfo> list = getData();
    // 获取所有客户信息中,联系人最多的数量。并以此数量作为行合并的依据
    int maxColNum = 1;
    for (CustomerInfo ele : list) {
        if (ele.getPersonList().size() > maxColNum) {
            maxColNum = ele.getPersonList().size();
        }
    }

    // 整理,讲List包含List的数据格式,改成List的格式
    final int finalMaxColNum = maxColNum;
    List<Customer> result = list.stream()
            .flatMap(ele -> {
                List<Customer> temp = new ArrayList<>();
                // 获取当前客户联系人的数量
                int len = ele.getPersonList().size();
                // 先添加一条完整的客户信息
                temp.add(new Customer(ele));
                for (int i = 1; i < finalMaxColNum; i++) {
                    if (i > len) {
                        // 达不到maxColNum的,补null占位。
                        temp.add(new Customer());
                    } else {
                        // 只添加联系人信息
                        temp.add(new Customer(ele.getPersonList().get(i)));
                    }
                }
                return temp.stream();
            }).collect(Collectors.toList());

    // 设置excel表头样式
    WriteSheet sheet = EasyExcel.writerSheet("客户信息").head(Customer.class).sheetNo(1).build();
    // 设置excel表格样式
    ExcelWriter writer = EasyExcel.write(response.getOutputStream()).needHead(true).excelType(ExcelTypeEnum.XLSX)
            .registerWriteHandler(new LoopMergeStrategy(maxColNum, 0))  // 设置第一列每maxColNum行合并
            .registerWriteHandler(new LoopMergeStrategy(maxColNum, 1))  // 设置第二列每maxColNum行合并
            .registerWriteHandler(new LoopMergeStrategy(maxColNum, 2))  // 设置第三列每maxColNum行合并
            .build();
    // 写入excel数据
    writer.write(result, sheet);
    // 通知浏览器以附件的形式下载处理,设置返回头要注意文件名有中文
    response.setHeader("Content-disposition", "attachment;filename=" + new String("客户信息列表".getBytes("gb2312"), "ISO8859-1") + ".xlsx");
    response.setContentType("multipart/form-data");
    response.setCharacterEncoding("utf-8");
    writer.finish();
}

2.3 getDate方法(用于模拟service层拿到的数据)

public static List<CustomerInfo> getData() {

    List<CustomerInfo> data = new ArrayList<>();

    CustomerInfo customer = new CustomerInfo();
    customer.setUserCode("CT_jx001");
    customer.setUserName("江西电信公司");
    customer.setAddress("江西省南昌市青山湖区");
    CustomerInfo.Person person1 = new CustomerInfo.Person("张三", "12345678910");
    CustomerInfo.Person person2 = new CustomerInfo.Person("李四", "10987654321");
    List<CustomerInfo.Person> personList = new ArrayList<>();
    personList.add(person1);
    personList.add(person2);
    customer.setPersonList(personList);
    data.add(customer);

    CustomerInfo customer2 = new CustomerInfo();
    customer2.setUserCode("CT_jx002");
    customer2.setUserName("广东电信公司");
    customer2.setAddress("广东省广州市花都区");
    CustomerInfo.Person person12 = new CustomerInfo.Person("小明", "12345678910");
    CustomerInfo.Person person22 = new CustomerInfo.Person("小红", "10987654321");
    CustomerInfo.Person person23 = new CustomerInfo.Person("小王", "12345678910");
    List<CustomerInfo.Person> personList2 = new ArrayList<>();
    personList2.add(person12);
    personList2.add(person22);
    personList2.add(person23);
    customer2.setPersonList(personList2);
    data.add(customer2);

    return data;
}

2.4 效果

接口是get类型的,浏览器直接访问,即可下载文件,效果如下。

顺便给出debug效果,方便理解。

 debug效果

三、问题及展望

从实现效果即可看出本方法的问题,就是会出现空行。

但是,这个空行避免不了,因为多行合并,只能按照最大值给,且不能动态调整。

这种方式,虽然可以实现复杂表头的导出,但显示不是令人满意的,主要是因为存在空行的问题。

我实在是找不出更好的解决方案,在此仅提供如下思路。

也你能通过EasyPoi的 自定义拦截器数据格式转换器模板写入合并单元格重复多次写入等功能实现。

四、参考链接

除官网地址外,参考如下文章。

Can not find ‘Converter‘ support class List问题解决_我取这个昵称总没被使用吧?的博客-CSDN博客问题描述com.alibaba.excel.exception.ExcelDataConvertException: Can not find ‘Converter’ support class List.问题解释EasyExcel开源框架中Converter接口的convertToExcelData只实现了转换BigDecimal、Bolean、Byte[]、btye[]、Byte、Date、Double、File、Float、InputStream、Integer、Long、Short、URL这些https://blog.csdn.net/qq_41049371/article/details/120156305EasyExcel ExcelDataConvertException:Can not find ‘Converter‘ support class ArrayList问题解决_旭东怪的博客-CSDN博客问题描述:com.alibaba.excel.exception.ExcelDataConvertException:Cannotfind'Converter'supportclassArrayList.问题分析:1、查看doWrite(List data)的源码时发现Converter接口的convertToExcelData只实现了转换BigDecimal、Bolean、Byte[]、btye[]、Byte、Date、Double、File、Float、InputStream、...https://blog.csdn.net/qq_38974638/article/details/116609844

猜你喜欢

转载自blog.csdn.net/qq_41057885/article/details/125316639
今日推荐