背景
SpringBoot的同步导出方式中,服务器会阻塞直到Excel文件生成完毕,这在处理大量数据的导出功能是效率很低,所以我们可以将导出数据拆分后利用CompletableFuture,将导出任务异步化,并行使用easyExcel导出多个excel文件,最后这些文件进一步压缩成ZIP格式以方便下载。
DEMO代码:
@RestController
@RequestMapping("/export")
public class ExportController {
@Autowired
private ExcelExportService excelExportService;
@GetMapping("/zip")
public ResponseEntity<byte[]> exportToZip() throws Exception {
List<List<Data>> dataSets = Lists.partition(dataList, 10);
List<CompletableFuture<String>> futures = new ArrayList<>();
// 异步导出所有Excel文件
String outputDir = "path/to/output/dir/";
for (List<Data> dataSet : dataSets) {
futures.add(excelExportService.exportDataToExcel(dataSet, outputDir));
}
// 等待所有导出任务完成
CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).get(10, TimeUnit.MINUTES);;
// 收集Excel文件路径
List<String> excelFilePaths = futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
// 压缩文件
File zipFile = new File("path/to/output.zip");
try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipFile))) {
for (String filePath : excelFilePaths) {
zipFile(new File(filePath), zipOut, new File(filePath).getName());
}
}
// 返回ZIP文件
byte[] data = Files.readAllBytes(zipFile.toPath());
return ResponseEntity.ok()
.header("Content-Disposition", "attachment; filename=\"" + zipFile.getName() + "\"")
.contentType(MediaType.parseMediaType("application/zip"))
.body(data);
}
// 将文件添加到ZIP输出流中
private void zipFile(File file, ZipOutputStream zipOut, String entryName) throws IOException {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))) {
ZipEntry zipEntry = new ZipEntry(entryName);
zipOut.putNextEntry(zipEntry);
byte[] bytesIn = new byte[4096];
int read;
while ((read = bis.read(bytesIn)) != -1) {
zipOut.write(bytesIn, 0, read);
}
zipOut.closeEntry();
}
}
}
异步并行生成excel文件,利用EasyExcel
库来简化Excel的生成过程:
@Service
public class ExcelExportService {
private static final String TEMPLATE_PATH = "path/to/template.xlsx";
@Autowired
private TaskExecutor taskExecutor;
public CompletableFuture<Void> exportDataToExcel(List<Data> dataList, String outputDir) {
Path temproaryFilePath = Files.createTempFile(outputDir, "excelFilePre",".xlsx");
return CompletableFuture.runAsync(() -> {
try (OutputStream outputStream = new FileOutputStream(temproaryFilePath )) {
EasyExcel.write(outputStream, Data.class)
.withTemplate(TEMPLATE_PATH)
.sheet()
.doFill(dataList)
.finish();
return temproaryFilePath.toString();
} catch (IOException e) {
throw new RuntimeException("Failed to export Excel file", e);
}
}, taskExecutor);
}
}
太强 ! SpringBoot中优化if-else语句的七种绝佳方法实战
太强 ! SpringBoot中出入参增强的5种方法 : 加解密、脱敏、格式转换、时间时区处理
flowable、activiti、camunda 三大工作流引擎技术选型指南
提升编程效率的利器: Google Guava库中双向映射BitMap
从MySQL行格式原理看:为什么开发规范中不推荐NULL?数据是如何在磁盘上存储的?
Java并发库(JUC)中LongAdder:高并发下基于分段锁和CAS的操作优化
提升编程效率的利器: Google Guava库之RateLimiter优雅限流
从MySQL行格式原理看:为什么开发规范中不推荐NULL?数据是如何在磁盘上存储的?
点赞+转发+在看+收藏 就是最大的支持!