最近做了一个导出功能,需要将页面筛选条件之后的数据全部导出到excel中,每个分公司只能导出自己账号权限下的数据不会很多,但是如果是总公司不进行任何筛选直接导出全部数据大概会有17万左右的数据,后期可能还会更多,但是前端超时时间是30s。由于easyexcel不支持并发写入,所以只能从数据库方面入手。
自定义线程池
由于需要使用到多线程,最好自定义一个导出专用的线程池不要影响到其他功能
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.ThreadPoolExecutor;
@Configuration
@EnableAsync
public class ThreadPoolConfiguration {
/**
* 导出任务线程池
* @return
*/
@Bean("exportTaskExecutor")
public ThreadPoolTaskExecutor exportTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(5);
// 最大线程数
executor.setMaxPoolSize(10);
// 等待队列数
executor.setQueueCapacity(0);
// 非核心线程空闲存活时间
executor.setKeepAliveSeconds(60);
// 线程名称
executor.setThreadNamePrefix("export-task-");
// 拒绝策略:由主线程继续执行被拒绝的任务
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(30);
// 线程池初始化
executor.initialize();
return executor;
}
}
多线程分页查询数据库并导出
// 设置文本内省
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
// 设置字符编码
response.setCharacterEncoding("utf-8");
String fileTimeStr = DateUtil.format(DateUtil.date(), "yyyyMMddHHmm");
String fileName = URLEncoder.encode("清单导出_" + fileTimeStr, StandardCharsets.UTF_8);
// 设置响应头
response.setHeader("Content-disposition", "filename=" + fileName + ".xlsx");
// 使用try-with-resources确保资源释放
log.info("开始添加多线程导出");
try (ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream(), ExportItemListVo.class)
.registerWriteHandler(new HorizontalCellStyleStrategy(CustomExcelStyleUtil.getHeadStyle(),CustomExcelStyleUtil.getContentStyle()))
.build()
) {
WriteSheet writeSheet = EasyExcel.writerSheet("标的清单").build();
String finalCompanyCode = companyCode;
// 提交当前批次任务
List<Future<List<ExportItemListVo>>> batchFutures = new LinkedList<>();
// 分页查询并写入
for (int pageNum = 1; pageNum <= totalPages; pageNum++) {
// 分页
Page<ExportItemListVo> page = new Page<>(pageNum,
pageSize);
page.setSearchCount(false);
if (totalPages == 1) {
// 当只有一页则不分页 加速查询
batchFutures.add(exportTaskExecutor.submit(() -> itemMapService.exportItemList(params, finalCompanyCode, null)));
} else {
batchFutures.add(exportTaskExecutor.submit(() -> itemMapService.exportItemList(params, finalCompanyCode, page)));
}
}
log.info("开始写入excel");
while (!batchFutures.isEmpty()) {
Future<List<ExportItemListVo>> future = batchFutures.remove(0);
excelWriter.write(future.get(30, TimeUnit.SECONDS), writeSheet);
}
}