优秀的编程知识分享平台

网站首页 > 技术文章 正文

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

nanyue 2025-01-16 20:21:20 技术文章 3 ℃

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);
    }
}
最近发表
标签列表