Java后台生成多个Excel并用Zip打包下载

下面是一个完整的实现方案,可以生成多个Excel文件,将它们放入不同的目录结构中,然后打包成ZIP文件供用户下载。


1. 添加必要的依赖

首先确保你的项目中包含以下依赖(Maven示例):

html 复制代码
<!-- Apache POI for Excel -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>5.2.3</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>5.2.3</version>
</dependency>

<!-- Zip工具 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-compress</artifactId>
    <version>1.21</version>
</dependency>

2. 实现代码

java 复制代码
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;

import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

public class ExcelZipExporter {

    public void exportMultipleExcelsAsZip(HttpServletResponse response) throws IOException {
        // 设置响应头
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=\"reports.zip\"");

        // 创建Excel文件集合,key为文件路径,value为工作簿
        Map<String, Workbook> excelFiles = new HashMap<>();
        
        // 生成第一个Excel - 放在根目录
        Workbook workbook1 = generateExcel("销售数据", new String[]{"产品", "数量", "金额"}, 
            new Object[][]{
                {"产品A", 100, 5000},
                {"产品B", 150, 7500},
                {"产品C", 200, 10000}
            });
        excelFiles.put("sales_report.xlsx", workbook1);
        
        // 生成第二个Excel - 放在monthly目录
        Workbook workbook2 = generateExcel("月度统计", new String[]{"月份", "收入", "支出"}, 
            new Object[][]{
                {"一月", 50000, 30000},
                {"二月", 55000, 32000},
                {"三月", 60000, 35000}
            });
        excelFiles.put("monthly/financial_report.xlsx", workbook2);
        
        // 生成第三个Excel - 放在yearly目录
        Workbook workbook3 = generateExcel("年度汇总", new String[]{"年份", "总收入", "总利润"}, 
            new Object[][]{
                {"2020", 500000, 150000},
                {"2021", 550000, 180000},
                {"2022", 600000, 200000}
            });
        excelFiles.put("yearly/summary_report.xlsx", workbook3);
        
        // 将Excel文件打包成ZIP并输出到响应流
        try (OutputStream out = response.getOutputStream();
             ZipArchiveOutputStream zos = new ZipArchiveOutputStream(out)) {
            
            for (Map.Entry<String, Workbook> entry : excelFiles.entrySet()) {
                // 创建ZIP条目
                ZipArchiveEntry zipEntry = new ZipArchiveEntry(entry.getKey());
                zos.putArchiveEntry(zipEntry);
                
                // 将Excel工作簿写入ZIP条目
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                entry.getValue().write(baos);
                zos.write(baos.toByteArray());
                
                // 关闭当前条目
                zos.closeArchiveEntry();
                
                // 关闭工作簿
                entry.getValue().close();
            }
            
            // 完成ZIP文件
            zos.finish();
        }
    }

    /**
     * 生成Excel工作簿
     * @param sheetName 工作表名称
     * @param headers 表头
     * @param data 数据
     * @return 生成的Workbook对象
     */
    private Workbook generateExcel(String sheetName, String[] headers, Object[][] data) {
        Workbook workbook = new XSSFWorkbook();
        Sheet sheet = workbook.createSheet(sheetName);
        
        // 创建表头行
        Row headerRow = sheet.createRow(0);
        for (int i = 0; i < headers.length; i++) {
            Cell cell = headerRow.createCell(i);
            cell.setCellValue(headers[i]);
            
            // 设置表头样式
            CellStyle headerStyle = workbook.createCellStyle();
            Font font = workbook.createFont();
            font.setBold(true);
            headerStyle.setFont(font);
            cell.setCellStyle(headerStyle);
        }
        
        // 填充数据
        for (int i = 0; i < data.length; i++) {
            Row row = sheet.createRow(i + 1);
            for (int j = 0; j < data[i].length; j++) {
                Object value = data[i][j];
                Cell cell = row.createCell(j);
                
                if (value instanceof String) {
                    cell.setCellValue((String) value);
                } else if (value instanceof Number) {
                    cell.setCellValue(((Number) value).doubleValue());
                }
            }
        }
        
        // 自动调整列宽
        for (int i = 0; i < headers.length; i++) {
            sheet.autoSizeColumn(i);
        }
        
        return workbook;
    }
}

3. 控制器调用示例

java 复制代码
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController
@RequestMapping("/api/reports")
public class ReportController {

    private final ExcelZipExporter excelZipExporter;
    
    public ReportController(ExcelZipExporter excelZipExporter) {
        this.excelZipExporter = excelZipExporter;
    }
    
    @GetMapping("/download")
    public void downloadExcelReports(HttpServletResponse response) throws IOException {
        excelZipExporter.exportMultipleExcelsAsZip(response);
    }
}

4. 功能说明

  1. 生成多个Excel文件:代码中可以创建任意数量的Excel文件,每个文件可以有不同的内容和结构。

  2. 目录结构:通过在文件名中包含路径(如"monthly/financial_report.xlsx"),可以在ZIP包中创建目录结构。

  3. 动态生成:所有Excel文件都是在内存中动态生成的,不需要临时文件。

  4. 直接下载:生成的ZIP文件直接通过HTTP响应流发送给客户端,实现下载功能。

5. 优化建议

  1. 大文件处理:如果Excel文件很大,可以考虑使用SXSSFWorkbook替代XSSFWorkbook以减少内存消耗。

  2. 错误处理:添加适当的错误处理和资源清理代码。

  3. 进度反馈:对于非常大的文件集合,可以考虑实现进度反馈机制。

  4. 压缩级别 :可以通过zos.setLevel(Deflater.BEST_COMPRESSION)设置压缩级别。

这个实现方案可以灵活地生成多个Excel文件并按照需要的目录结构打包下载,适用于各种报表导出场景。

相关推荐
梦想平凡5 分钟前
情怀棋牌源代码焕新记录(一):全新国风UI与原版工程梳理
java·服务器·数据库·websocket·网络协议·cocos2d
SEO_juper5 分钟前
2026年用Python分析网站访问日志:看清Googlebot和AI爬虫怎么爬你的站(附完整代码)
开发语言·前端·seo·独立站·谷歌优化
ba_pi27 分钟前
springAI2.0接入mcp读取mysql
java·agent·spring ai
宸津-代码粉碎机33 分钟前
Java面试核心:JVM三大GC垃圾回收算法原理与生产场景落地分析
java·大数据·人工智能·python·spring
Ivanqhz35 分钟前
BPE(Byte Pair Encoding) 算法
java·服务器·网络·深度学习·神经网络
愛芳芳39 分钟前
Swagger2 多环境动态配置实战指南
java·后端·maven·springboot·swagger
huaweichenai42 分钟前
spring boot实现任务异步处理(队列)
java·spring boot
MZA6661 小时前
实战:基于 XXL-JOB + 策略模式 + Nacos 配置化的 CSV 导出任务系统设计
java·spring boot·mybatis
邪修king1 小时前
Re:Linux 系统篇(二十九):动静态库Chapter2:动态库深度辨析 —— 核心本质、制作流程、双阶段查找模型与排错指南
android·java·linux·开发语言
泡海椒2 小时前
告别 iText 繁杂配置:jquick-pdf 极简 PDF 生成实战(零基础上手)
java·开发语言·pdf