easyExcel的多线程导出

1、导出的核心方法:

import cn.com.cgnpc.aep.bizcenter.common.service.impl.FileExportContext;
import cn.com.cgnpc.aep.bizcenter.common.service.impl.FileExportService;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
 
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
 
@Component
public class ExcelExportUtil {
 
    @Resource
    @Qualifier("excelThreadPool")
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
 
    /**
     * @param totalCount 总记录条数
     * @param clazz 导出的Excel对象
     * @param service 具体实现查询数据的服务类
     * @param map 查询参数
     */
    public void exportExcel(HttpServletResponse response,int totalCount, Class<?> clazz, FileExportService service, Map<String,Object> map) throws Exception{
 
        FileExportContext context = new FileExportContext(service);
 
        //文件名
        String fileName = String.valueOf(System.currentTimeMillis());
 
        //ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        OutputStream outputStream = null;
        try {
            //每一个Sheet存放1w条数据
            Integer sheetDataRows = 100;
            //每次写入的数据量5000,每页查询5000
            Integer writeDataRows = 50;
            //计算需要的Sheet数量
            Integer sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);
            //计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)
            Integer oneSheetWriteCount = sheetDataRows / writeDataRows;
            //计算最后一个sheet需要写入的次数
            Integer lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount : totalCount % sheetDataRows % writeDataRows == 0 ? totalCount % sheetDataRows / writeDataRows : (totalCount % sheetDataRows / writeDataRows)+1;
 
            outputStream = response.getOutputStream();
 
            //必须放到循环外,否则会刷新流
            ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
 
            Map<Integer,List<List<String>>> pageMap = new ConcurrentHashMap<>(Math.toIntExact(sheetNum));
 
            CountDownLatch countDownLatch = new CountDownLatch(Math.toIntExact(sheetNum));
            // 多线程查询参数Map
            Map<Integer,Map<String,Object>> queryMap = new ConcurrentHashMap<>();
 
            //开始分批查询分次写入 sheetNum
            for (int i = 0; i < sheetNum; i++) {
                //创建Sheet
                WriteSheet sheet = new WriteSheet();
                sheet.setSheetName("Sheet"+i);
                sheet.setSheetNo(i);
                int finalNum = i;
                threadPoolTaskExecutor.submit(()->{
                    ConcurrentHashMap<String,List<List<String>>> dataListMap = new ConcurrentHashMap<>();
                    //循环写入次数, j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCount
                    for (int j = 0; j < (finalNum != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++) {
                        int finalJ = j;
                        queryMap.put(finalNum,new HashMap<String,Object>(){
                        {
                put("page",finalNum * sheetDataRows + finalJ * writeDataRows);
                put("pageSize",writeDataRows);
                putAll(map);
    }
});
                        // 策略模式调用查询
                    List<List<String>> dataList = Optional.ofNullable(pageMap.get(finalNum)).orElse(new ArrayList<>());
                    dataList.addAll(context.queryPageExcel(queryMap.get(finalNum)));
                    pageMap.put(finalNum,dataList);    
                    }
                    
                    countDownLatch.countDown();
                });
            }
            try{
                countDownLatch.await();
            }catch (Exception e){
                e.printStackTrace();
            }
            // 关闭线程
            threadPoolTaskExecutor.shutdown();
 
            pageMap.forEach((k,v)->{
                // 分sheet保存数据
                WriteSheet writeSheet = EasyExcel.writerSheet(k, "Sheet" + (k + 1)).head(clazz)
                        .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
                excelWriter.write(v,writeSheet);
                pageMap.remove(k);
            });
 
            // 下载EXCEL,返回给前端stream流
            response.setContentType("application/octet-stream");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
            excelWriter.finish();
            outputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if (outputStream != null) {
                outputStream.close();
            }
        }
        //return new ByteArrayInputStream(outputStream.toByteArray());
    }
 
}

多线程配置:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
import java.util.concurrent.ThreadPoolExecutor;
 
@Configuration
public class ConcurrentThreadGlobalConfig {
 
    @Bean("excelThreadPool")
    public ThreadPoolTaskExecutor defaultThreadPool(){
        // 创建线程池
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数
        executor.setCorePoolSize(Runtime.getRuntime().availableProcessors()+1);
        // 最大线程数
        executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors()+1);
        // 队列中的最大数目
        executor.setQueueCapacity(600);
        // 线程名称的前缀
        executor.setThreadNamePrefix("defaultThreadPool_");
        // 线程拒绝策略,由调用者决定
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 空闲线程时间
        executor.setKeepAliveSeconds(60);
        // 加载线程
        executor.initialize();
        return executor;
    }
 
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值