easyPoi导出(分页将数据写入sheet)多sheet页(干货)

文章介绍了在处理大量数据时,如何使用Easypoi库进行分页查询并分批写入Excel,以避免内存溢出和提高查询效率。作者提供了代码示例,包括计算总记录数、确定每个Sheet的大小以及执行导出操作的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一:问题来源

        首先为什么需要分页将数据写入sheet

  1. 如果把所有数据一次性装载到内存,很容易引起OOM。
  2. 如果数据量太大,查询sql语句肯定很慢
  3. 所以目前我采用,分页的方式进行查询,然后单次写入的数据就是查询出来的结果

二:代码示例

        1.首先构建公共类


/**
 * easypoi导出模板
 * @param <T>  查询的入参类型
 * @param <R>  导出的结果类型
 */
public abstract class BaseEasyPoiExport<T,R> {


    @Resource
    private FilePluginFactory filePluginFactory;

    /**
     * 计算导出数据的总数
     */
    protected abstract Long dataTotalCount(T conditions);

    /**
     * 导出到excel,支持大数量的分批写入
     */
    protected void exportExcel(String fileName, T conditions) {
        //根据条件查询总记录
        Long totalCount = dataTotalCount(conditions);
        if (totalCount == 0){
            throw new ServiceException("没有数据导出");
        }
        //每一个Sheet存放的记录数
        Long sheetDataRows = eachSheetTotalCount();
        //每次写入的数据量
        Long writeDataRows = eachTimesWriteSheetTotalCount();
        if (totalCount < sheetDataRows) {
            sheetDataRows = totalCount;
        }
        if (sheetDataRows < writeDataRows) {
            writeDataRows = sheetDataRows;
        }
        doExport(fileName, conditions, totalCount, sheetDataRows, writeDataRows);
    }

    /**
     * 导出到excel
     */
    private  void doExport(String fileName, T conditions, Long totalCount, Long sheetDataRows, Long writeDataRows) {
        CompletableFuture.runAsync(() -> {
            // 计算需要的Sheet数量
            Long sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
            // 计算一般情况下每一个Sheet需要写入的次数
            Long oneSheetWriteCount = totalCount > sheetDataRows ? sheetDataRows / writeDataRows :
                    totalCount % writeDataRows > 0 ? totalCount / writeDataRows + 1 : totalCount / writeDataRows;
            // 计算最后一个sheet需要写入的次数
            Long lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount :
                    (totalCount % sheetDataRows % writeDataRows == 0 ? (totalCount / sheetDataRows / writeDataRows) :
                            (totalCount / sheetDataRows / writeDataRows + 1));
            // 每一个sheet储存的数据
            List<Map<String, Object>> sheetsList = new ArrayList<>();
            //sheet页的次数
            for (int i = 0; i < sheetNum; i++) {
                String sheetName = "sheet" + i;
                List<R> orderExportVoList = new ArrayList<>();
                //如果不是最后一次的写入量,那么就是每次的写入量
                for (int j = 0; j < (i != sheetNum - 1 || i == 0 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
                    buildDataList(orderExportVoList, conditions, j + 1 + oneSheetWriteCount * i, writeDataRows);
                }
                if (CollectionUtil.isNotEmpty(orderExportVoList)) {
                    Map<String, Object> exportMap = Maps.newHashMap();
                    ExportParams exportParams = new ExportParams(null, sheetName, ExcelType.HSSF);
                    // 以下3个参数为API中写死的参数名 分别是sheet配置/导出类(注解定义)/数据集
                    exportMap.put("title", exportParams);
                    exportMap.put("entity", OrderExportVo.class);
                    exportMap.put("data", orderExportVoList);
                    // 加入多sheet配置列表
                    sheetsList.add(exportMap);
                }
            }
            Workbook workbook = ExcelExportUtil.exportExcel(sheetsList, ExcelType.HSSF);
            String filePath = filePluginFactory.filePlugin().getFileNamePath(workbook,fileName);
            System.out.println(filePath);
        });
    }



    /**
     * 每一个sheet存放的数据总数
     * 期望是偶数,奇数还未完善
     */
    protected abstract Long eachSheetTotalCount();

    /**
     * 每次写入sheet的总数
     * 期望是偶数,奇数还未完善
     */
    protected abstract Long eachTimesWriteSheetTotalCount();

    /**
     * 构建每次查询数量
     */
    protected abstract void buildDataList(List<R> resultList, T conditions, Long pageNo, Long pageSize);
}

1.首先将查询条件进行count,看是否有数据

2.计算需要的sheet数,和每次写入的数量

3.实现代码逻辑,分页查询,每次查询的数量等于写入的数量

4.最后将查询出来的数据进行构建,组装成想要导出的数据

5.实现上传阿里云



@Component
public class OrderExportServiceImpl extends BaseEasyPoiExport<OrderExportParams,OrderExportVo> {


    @Autowired
    private OrderService orderService;


    /**
     * 数据导出
     */
    public void exportWithBigData(String fileName, OrderExportParams conditions) {
        this.exportExcel(fileName, conditions);
    }


    @Override
    protected Long dataTotalCount(OrderExportParams conditions) {
        Long count = orderService.count(new LambdaQueryWrapper<Order>()
                .eq(Order::getDeleteFlag, false)
                .eq(StringUtils.isNotBlank(conditions.getShopName()), Order::getShopName, conditions.getShopName()));
        return count;
    }

    @Override
    protected Long eachSheetTotalCount() {
        return 5000L;
    }

    @Override
    protected Long eachTimesWriteSheetTotalCount() {
        return 2000L;
    }

    @Override
    protected void buildDataList(List<OrderExportVo> resultList, OrderExportParams conditions, Long pageNo, Long pageSize) {
        conditions.setPageNumber(pageNo.intValue());
        conditions.setPageSize(pageSize.intValue());
        Page<Order> page = orderService.page(PageUtil.initExportPage(conditions), new LambdaQueryWrapper<Order>()
                .eq(Order::getDeleteFlag, false)
                .eq(StringUtils.isNotBlank(conditions.getShopName()), Order::getShopName, conditions.getShopName())
                .select(Order::getShopName,Order::getOrderNum,Order::getLogisticsNo));
        List<Order> records = page.getRecords();
        if (CollectionUtil.isEmpty(records)){
            return;
        }
        for (Order record : records) {
            OrderExportVo orderExportVo = new OrderExportVo();
            BeanUtil.copyProperties(record,orderExportVo);
            resultList.add(orderExportVo);
        }
    }


}

三:总结 

此次的导出结果其实和上一篇的效果是一样的,可以去看easyPoi导出多sheet页(干货)-优快云博客

当然这里面可能也有一些不足之处,希望各位大佬能纠正 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值