优雅处理大量数据导出:easyPoi重构指南

优雅处理大量数据导出:easyPoi重构指南

1. 问题分析

在大规模数据导出中,存在以下问题:

  • 代码冗余: 传统的导出代码可能包含大量样板代码,使得代码冗余度高。
  • 可读性差: 大量重复的导出逻辑使代码难以理解和维护。
  • 导出速度慢: 未优化的导出代码可能导致性能问题,尤其在处理大量数据时。

2. 引入easyPoi依赖

<dependency>
    <groupId>cn.afterturn</groupId>
    <artifactId>easypoi-web</artifactId>
    <version>4.4.0</version>
</dependency>

3. easyPoi重构步骤

4.1 重构工具类代码

package com.ruoyi.common.core.utils;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.export.ExcelBatchExportService;
import org.apache.poi.ss.usermodel.Drawing;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExcelExportServiceProcessor extends ExcelBatchExportService {

    private Workbook workbook;
    private Sheet sheet;
    private List<ExcelExportEntity> excelParams;
    private ExportParams entity;
    private int titleHeight;
    private Drawing patriarch;
    private short rowHeight;
    private volatile int index;
    private ExecutorService executorService;

    // 默认线程池大小为10
    private static final int DEFAULT_POOL_SIZE = 10;

    public ExcelExportServiceProcessor() {
        this.executorService = Executors.newFixedThreadPool(DEFAULT_POOL_SIZE);
    }

    /**
     * 带参数的构造方法
     *
     * @param poolSize 线程池大小
     */
    public ExcelExportServiceProcessor(int poolSize) {
        this.executorService = Executors.newFixedThreadPool(poolSize > 0 ? poolSize : DEFAULT_POOL_SIZE);
    }

    /**
     * 获取Workbook对象
     *
     * @return Workbook对象
     */
    public Workbook getWorkbook() {
        return this.workbook;
    }

    /**
     * 初始化并导出大数据Excel文件
     *
     * @param entity       ExportParams对象
     * @param server       IExcelExportServerPostProcessor对象
     * @param queryParams  查询参数
     * @param <T>          泛型类型
     * @return Workbook对象
     */
    public <T> Workbook initExportBigExcel(ExportParams entity, IExcelExportServerPostProcessor server, Object queryParams) {
        int page = 1;
        int pageAfter = page + 1;
        int i = 1;
        List<CompletableFuture<Void>> futureList = new ArrayList<>();

        // 循环从数据库获取数据,直到获取到空数据为止
        while (true) {
            List<T> list = server.selectListForExcelExportForT(queryParams, page);
            if (list == null || list.isEmpty()) {
                break;
            }

            List<T> finalList = list;
            if (i == 1 && finalList != null && !finalList.isEmpty()) {
                init(entity, finalList.get(0).getClass());
            }

            // 多线程异步写入excel
            CompletableFuture<Void> future = CompletableFuture.runAsync(() -> writeData(finalList), executorService);
            futureList.add(future);
            i++;

            // 如果有停止标志,跳出循环
            if (queryParams instanceof Map) {
                Map<String, Object> map = (Map<String, Object>) queryParams;
                Object isPageStop = map.get("isPageStop");
                if (isPageStop != null) {
                    break;
                }
            }
            page = pageAfter++;
        }

        // 等待异步线程结束
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()])).join();
        return this.close();
    }

    /**
     * 写入数据的异步任务
     *
     * @param data 数据
     */
    private void writeData(List<?> data) {
        this.index += this.createCells(this.patriarch, this.index, data, this.excelParams, this.sheet, this.workbook, this.rowHeight, 0)[0];
    }

    /**
     * 关闭Excel文件
     *
     * @return Workbook对象
     */
    public Workbook close() {
        if (this.entity.getFreezeCol() != 0) {
            this.sheet.createFreezePane(this.entity.getFreezeCol(), this.titleHeight, this.entity.getFreezeCol(), this.titleHeight);
        }

        this.mergeCells(this.sheet, this.excelParams, this.titleHeight);
        this.addStatisticsRow(this.getExcelExportStyler().getStyles(true, (ExcelExportEntity) null), this.sheet);
        executorService.shutdownNow(); // 关闭线程池,尝试停止所有执行中的线程
        return this.workbook;
    }
}



    

4.2 创建工具类代码

import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.excel.entity.TemplateExportParams;
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
import cn.afterturn.easypoi.excel.export.ExcelBatchExportService;
import cn.afterturn.easypoi.excel.export.template.ExcelExportOfTemplateUtil;
import cn.afterturn.easypoi.handler.inter.IExcelExportServer;
import java.util.Collection;
import java.util.List;
import java.util.Map;

public class ExcelExportUtil {

    private ExcelExportUtil() {
    }
    /**
     * easypoi导出大数据量数据
     * @param entity
     * @param server
     * @param queryParams
     * @return
     */
    public static Workbook exportBigExcel(ExportParams entity, IExcelExportServerPostProcessor server, Object queryParams) {
        ExcelBatchExportServicePostProcessor batchServer = new ExcelBatchExportServicePostProcessor();
        return batchServer.initExportBigExcel(entity,server,queryParams);
    }
}

4.2 调用实现

4.2.1 HighDeptInAndOutController
@PostMapping("/export")
    public void export(HttpServletResponse response, @RequestParam Map<String,Object> params) throws IOException
    {
        params.keySet().removeIf(key->"pageSize,pageNum".indexOf(key) != -1);
        highDeptInAndOutService.export(response,params);
    }
4.2.1 IHighDeptInAndOutService
    /**
     * 导出
     * @param response
     * @param params
     */
    void export(HttpServletResponse response, Map<String, Object> params);
4.2.2 接口实现
@Override
    public void export(HttpServletResponse response, Map<String, Object> params) {
        int pageSizeSelf = 50000;
        ExportParams exportParams = new ExportParams("我开着法拉利", "我开着法拉利", ExcelType.XSSF);
        Workbook workbook = null;
        ServletOutputStream outputStream = null;
        workbook = ExcelExportCustomUtil.exportBigExcel(exportParams, new IExcelExportServerPostProcessor() {
            @Override
            public <T, U> List<T> selectListForExcelExportForT(U queryParams, int pageNum) {
                List<T> tList = exportTask(pageNum, pageSizeSelf, (Map<String, Object>) queryParams);
                //如果查询结果为空或者查询结果小于每页条数,则停止分页 这个使用的盘空是
                //hutool 工具类有需要自行引用
                if (CollectionUtils.isEmpty(tList) || tList.size() <pageSizeSelf) {
                    ((Map<String, Object>) queryParams).put("isPageStop", true);
                }
                return tList;
            }
        }, params);
        try {
            response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode("高值科室进销存.xls", "UTF-8"));
            outputStream = response.getOutputStream();
            workbook.write(outputStream);
            outputStream.close();
            workbook.close();
        } catch (IOException e) {
            throw new CustomException("导出失败", e.getMessage());

        }
    }
4.2.3 查询数据方法


  public <T> List<T> exportTask(int pageNum, int pageSize, Map<String, Object> params) {
        int offset = (pageNum - 1) *pageSize;
        //com.github.pagehelper:pagehelper 分页插件自行引用或者这个改为MyBatis-Plus自行更改
        PageHelper.offsetPage(offset, pageSize,false);
        List<HighDeptInAndOut> list = highDeptInAndOutMapper.exportList(params);
        return (List<T>)list;
    }

5. 总结

这个代码导出速度比程序员喝咖啡的速度还快。这个指南就像是easyPoi的炸鸡秘籍,让你在Excel的厨房里煮出美味的数据料理!实体类代码?那就像是我的“独家秘方”,不发,如果你有更酷的优化,别吝啬,大声说出来,我们一起玩转这个代码美食世界!有问题?滴滴作者,我们等着你的疑问,就像是等待新一季剧集的粉丝一样。如果有更好的优化,就像是技术男们的大杀器,不要藏着掖着,拿出来让大家都看看吧!

  • 13
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: easypoi是一款Java的POI扩展库,可以方便地将Java对象转换为Excel、Word、PDF等格式的文档。使用easypoi导出Excel大量数据非常简单,只需要将数据封装成Java对象,然后使用easypoi提供的API即可。同时,easypoi还支持Excel的样式设置、图片插入等功能,可以满足各种导出需求。 ### 回答2: Easypoi是一款基于POI的Java开发框架,它可以简化与Microsoft Excel文档交互的过程,提供了易于使用的API,使开发人员能够快速构建和导出Excel文档。 在导出大量数据的情况下,Easypoi的性能表现非常出色,它采用了多线程和文件流的方式进行数据处理和输出,大大提高了导出效率,避免了出现内存溢出等问题。 Easypoi可以支持多种数据源的导出,包括List、Map、JavaBean等,开发人员可以根据具体业务需求选择合适的数据源进行导出。同时,Easypoi提供了丰富的样式设置和模板功能,通过对样式的设置和模板文件的使用,可以实现复杂数据导出。 在使用Easypoi进行大量数据导出的过程中,需要注意以下几点: 1. 数据的准备:在导出之前需要确保数据完整、正确。若数据源较大,可以考虑进行分页处理。 2. 导出样式的设置:Easypoi提供了多种样式设置的方法,如字体、颜色、边框等,可以通过设置样式让导出Excel文档更加美观。 3. 模板的设计与使用:如果需要导出数据比较复杂,可以考虑使用模板,将数据填充到模板中,模板文件中的样式和格式将会在导出文档中自动保留。 4. 文件流和多线程的使用:为了提高导出效率并避免内存溢出等问题,可以采用文件流和多线程的方式进行数据处理和输出。 总之,Easypoi作为一款高效、易用的Excel导出框架,为我们提供了便利的开发和导出Excel文档的方法。在使用Easypoi导出大量数据时,需要注意一些细节,以确保导出的文档准确完整、美观清晰。 ### 回答3: EasyPoi是一款基于Apache POI封装的Java Excel操作工具,它提供了简单易用的API,使得导出Excel变得非常简单方便。在处理大量数据时,EasyPoi也是非常适用的。 首先,在导出大量数据时,需要使用PoiBaseView来创建Excel对象。它可以优化内存使用,避免内存溢出的问题。我们可以通过以下代码来创建PoiBaseView: ``` public class MyExcelView extends PoiBaseView { @Override protected void buildExcelDocument(Map<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { // 将数据写入Excel,以下省略 } } ``` 其次,我们需要设置分页导出,即每次导出指定数量的数据,以免一次性导出过多数据导致内存溢出。EasyPoi提供了一个非常方便的分页导出工具类,我们可以通过以下代码来设置分页: ``` ExportParams params = new ExportParams("标题", "表名"); params.setType(ExcelType.XSSF); // 设置Excel格式,XSSF为xlsx,HSSF为xls params.setSheetName("Sheet1"); // 设置Sheet名 params.setCreateHeadRows(true); // 设置是否创建头部信息 params.setFreezePane(1, 1); // 冻结第一行 params.setPageNum(10000); // 每次导出10000条数据 ``` 最后,我们需要配置导出数据的格式。EasyPoi提供了非常丰富的注解来配置导出数据格式,比如@Excel、@ExcelIgnore等。我们可以通过以下代码来配置导出数据格式: ``` public class User { @Excel(name = "ID", width = 25) private String id; @Excel(name = "姓名", width = 25) private String name; @Excel(name = "年龄", width = 25) private Integer age; //省略get/set方法 } ``` 以上是针对EasyPoi导出大量数据的简单介绍。EasyPoi提供了方便易用的接口,处理大量数据时也能得到非常好的性能表现。在实际应用中,我们可以根据具体需求进行更加详细的配置,以满足不同场景下的需求。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值