springboot整合EasyExcel导入导出,动态表头导出

1,294 阅读4分钟

本文参考文档:
SpringBoot整合EasyExcel实现
Java+EasyExcel实现文件导入导出,导入导出如此简单

导出导入一般使用阿里巴巴的easyexcel 阿帕奇的poi; 简单的一般建议使用easyexcel使用更方便,excel单元格合并等复杂操作推荐使用poi

这里

本篇文章使用easyexcel讲解导入导出

1. 引入pom依赖

        <!--easyExcel-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
        </dependency>

2.excel文件导出

导出时要设置导出文件名已经编码格式等,这些代码抽取出来封装为工具类

public class ExportUtil {

    public static void setFilename(HttpServletResponse response, String prefix) throws IOException {
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        String fileName = URLEncoder.encode(prefix + "_" + DateFormatUtils.format(new Date(), "yyyyMMddHHmmss"), "UTF-8");
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + fileName + ".xlsx");
    }

}

导出时要考虑数据量的问题,建议使用分页查询,或者游标分页,循环写入流中;
示例中导出的不是实体类,而是Map
所以这里表头是在代码中封装,没有在实体类添加注解@ExcelProperty("商品编号")
这里用Map是因为要做动态的导出,代码中有些是我的特殊逻辑,不用关注,后面我会写段伪代码方便大家看

public void exportBK(HttpServletResponse response, OverviewSearch search) throws IOException, ParseException {
       
       // bkObjId可以理解为是哪个表的唯一ID, 这里不用关注
        String bkObjId = search.getBkObjId();
        
        //每个List<String>是一个表头字段,用List<List<String>>表头有序
        // 并且查询表格数据后,每条数据中的字段处理,与head的有序集合顺序一致
        List<List<String>> head = new ArrayList<>();
        List<String> propertyIds;
        
        //我的表格是动态表头,每个人可以自定义选择表头,每个人的表头会保存到数据库
        //这里是查询当前用户 当前表格的表头字段
        List<ObjAttDes> objAttDes = objAttDesService.searchByBkObjId(bkObjId);
        
        //封装表头数据类型 List<List<String>>
        propertyIds = objAttDes.stream().map(ObjAttDes::getBkPropertyId).collect(Collectors.toList());
        objAttDes.forEach(objAttDe -> {
            List<String> list = new ArrayList<>();
            list.add(objAttDe.getBkPropertyName());
            head.add(list);
        });

        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).head(head).build();
        //新建一个空的Excel工作簿,填充值后将值写入流中
        WriteSheet writeSheet = EasyExcel.writerSheet(bkObjId).build();

        try {
            int pageNum = 1;
            int pageSize = 200;
            while (true){
                //设置分页
                search.setPageNum(pageNum);
                search.setPageSize(pageSize);
                //分页查询
                List<Map<String, Object>> info = overviewService.searchBKInst(search).getInfo();
                
                //我的代码特殊逻辑,做了很多特殊处理,例如时间,数组,枚举,不用关注
                objAttDesService.formatInst(search.getBkObjId(), info);
                
                // 查询出来的数据,但是用户自定义的表头是部分字段
                //按照用户保存表头字段顺序,重新组织数据 和表头照应, 这里List<Object>就是一条数据
                List<List<Object>> dataList = new ArrayList<>();
                info.forEach(map -> {
                    List<Object> list = new ArrayList<>();
                    for (String propertyId : propertyIds) {
                        Object value = map.get(propertyId);
                        list.add(value);
                    }
                    dataList.add(list);
                });
                //工作簿填充数据 填充值后将值写入流中
                excelWriter.write(dataList, writeSheet);
                //查询结果数据,不满pageSize 结束循环
                if(info.size() < pageSize){
                    break;
                }
                //翻页
                pageNum++;
            }
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }

    }

伪代码示例:

        ExportUtil.setFilename(response, "导出文件名");
        
	 //每个List<String>是一个表头字段,用List<List<String>>表头有序
        // 并且查询body数据后,每条数据中的字段处理,与head的有序集合顺序一致
        List<List<String>> head = new ArrayList<>();
        List<List<Object>> dataList = new ArrayList<>();
        //这里设置单元格样式字体策略等
        ExcelWriter excelWriter = EasyExcel.write(httpServletResponse.getOutputStream()).head(head).build();
        //工作簿名称可以不设置
        WriteSheet writeSheet = EasyExcel.writerSheet("工作簿名称").build();
        try {
            int start = 0;
            int limit = 500;
            while (true) {
                //查询数据
                List<Map<String, Object>> maps = findPage(start,limit);
                if (maps.isEmpty()) {
                    break;
                }
                excelWriter.write(dataList, writeSheet);
                start += limit;
            }
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }

  当然如果你导出的不是动态表头的表格,完全可以用实体类映射,下面我用实例类注解方式@ExcelProperty封装一个通用的导出组件,当然你也可以仿照@ExcelProperty 自定义一个注解;
        这个组件没有做分页查询当数据量大时,会存在问题,大家可以自己改造写,这里我就不再搞了,如果有需要可以给我评论,后面我再加上


@Component
@Slf4j
@ServiceMethodLog(printParams = false)
public class ExportComponent {


    @Autowired
    ObjectMapper objectMapper;

    public void export(HttpServletResponse response, Supplier supplier) throws Exception {
        //获取数据 这里使用供给式函数接口 参数例如 ()-> xxxMapper.queryDetails()
        List dbData = supplier.get();
        
        
        if (CollectionUtils.isEmpty(dbData)) {
            throw new ApplicationException(ExceptionEnum.SPOT_BUGS_METHOD_RETURN_NULL);
        }
        Field[] fields = dbData.get(0).getClass().getDeclaredFields();
        List<String> headIds = new ArrayList<>(fields.length);
        //表头
        List<List<String>> head = new ArrayList<>(fields.length);
        //数据
        List<List<Object>> data = new ArrayList<>(dbData.size());

        //封装表头
        for (Field field : fields) {
            field.setAccessible(true);
            ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
            if (annotation == null || StringUtils.isBlank(annotation.value()[0])) {
                continue;
            }
            head.add(Collections.singletonList(annotation.value()[0]));
            headIds.add(field.getName());
        }
        //封装数据
        List<Map<String, Object>> maps = objectMapper.readValue(objectMapper.writeValueAsString(dbData), new TypeReference<List<Map<String, Object>>>() {
        });
        maps.forEach(map -> {
            List<Object> list = new ArrayList<>(fields.length);
            for (String headId : headIds) {
                list.add(map.get(headId));
            }
            data.add(list);
        });

        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).head(head).build();
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        try {
            excelWriter.write(data, writeSheet);
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }
}

这种导出非常适合做游标查询,游标查询可参考文章完成www.cnblogs.com/jingzh/p/17…