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文件并按照需要的目录结构打包下载,适用于各种报表导出场景。

相关推荐
cui_ruicheng几秒前
Linux线程(四):线程池、日志系统与单例模式
linux·开发语言·单例模式
伊甸33 分钟前
Neo4j 常用语法速查(Cypher)
java·数据库·neo4j
小程故事多_805 分钟前
深度解析Claude Code,AI编码助手的底层架构与工作原理
java·人工智能·架构·智能体
文祐7 分钟前
三维数组在内存中的分布
开发语言·内存
通往曙光的路上9 分钟前
JUCJUCJUC
java·前端·数据库
kaikaile199510 分钟前
基于 MATLAB 的3D 蒙特卡洛光子传输模拟
开发语言·matlab·3d
吴声子夜歌14 分钟前
Java——ArrayList
java·arraylist
旷世奇才李先生15 分钟前
Java 内置HttpClient 深度实战与性能优化全指南
java
我是唐青枫15 分钟前
C#.NET YARP 认证授权实战:在网关层统一接入 JWT
开发语言·c#·.net
故事和你9118 分钟前
洛谷-【数据结构2-2】线段树2
开发语言·数据结构·算法·动态规划·图论