SpringBoot使用EasyExcel并行导出多个excel文件并压缩zip下载

文摘   2024-09-15 20:19   广东  
背景

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 三大工作流引擎技术选型指南

微服务中token鉴权设计的4种方式总结

90分掌握一门语言:lua脚本基础到高级教程

提升编程效率的利器: Google Guava库中双向映射BitMap

从MySQL行格式原理看:为什么开发规范中不推荐NULL?数据是如何在磁盘上存储的?

Java并发库(JUC)中LongAdder:高并发下基于分段锁和CAS的操作优化

提升编程效率的利器: Google Guava库之RateLimiter优雅限流

玩转同步控制:LockSupport深入解读

必知必会!MySQL索引下推:原理与实战

MySQL 索引优化:深入自适应哈希索引的奥秘

从MySQL行格式原理看:为什么开发规范中不推荐NULL?数据是如何在磁盘上存储的?

结合MySQL成本模型的查询性能优化实战

关注『 码到三十五 』,日有所获

 点赞+转发+在看+收藏 就是最大的支持!

码到三十五
主要分享正经的开发技术(原理,架构,实践,源码等),以输出驱动输入;当然偶尔会穿插点生活琐碎,顺便吃个瓜,目的嘛,搞点精准流量,看能不能发发广告。
 最新文章