You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中如何用单个Controller同时生成多份不同列CSV文件下载

问题原因

单个HTTP请求只能对应一个响应,每个响应仅支持返回一个文件。你在同一个接口中往同一个HttpServletResponse的输出流里写入两份CSV内容,自然会出现内容拼接的情况。

解决方案

提供两种常用实现方式:

方案1:前端发起两次独立导出请求(改动最小,推荐)

将公共CSV导出逻辑抽为通用工具方法,提供两个导出接口(或同一个接口加类型参数区分导出字段),前端分别调用两个接口即可得到两个独立CSV文件。

代码示例

// 抽离公共CSV导出工具方法
private void writeCsvToResponse(List<String> includeFields, String fileName, List<Report> data, HttpServletResponse response) throws Exception {
    response.setContentType("text/csv");
    response.setHeader(HttpHeaders.CONTENT_DISPOSITION, 
            "attachment; filename=\"" + fileName + "\"");
    StatefulBeanToCsvBuilder<Report> builder = new StatefulBeanToCsvBuilder<Report>(response.getWriter())
            .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
            .withSeparator(CSVWriter.DEFAULT_SEPARATOR)
            .withOrderedResults(true);
    // 过滤排除不需要导出的字段
    Arrays.stream(Report.class.getDeclaredFields())
            .filter(field -> !includeFields.contains(field.getName()))
            .forEach(field -> builder.withIgnoreField(Report.class, field));
    builder.build().write(data);
}

// 导出id+amount的接口
@GetMapping("/export/id-amount")
public void exportIdAmount(@RequestParam(name="cohort") String cohort, HttpServletResponse response) throws Exception {
    List<Report> data = reportsService.findByCohort(cohort);
    writeCsvToResponse(List.of("id", "amount"), "liabilities_id_amount.csv", data, response);
}

// 导出name+currency的接口
@GetMapping("/export/name-currency")
public void exportNameCurrency(@RequestParam(name="cohort") String cohort, HttpServletResponse response) throws Exception {
    List<Report> data = reportsService.findByCohort(cohort);
    writeCsvToResponse(List.of("name", "currency"), "liabilities_name_currency.csv", data, response);
}

方案2:后端打包两个CSV为ZIP单次返回

如果要求单次请求就能拿到两个文件,可以将两份CSV打包为ZIP压缩包返回,解压后即可得到两个独立文件。

代码示例

import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import java.nio.charset.StandardCharsets;

@GetMapping("/export/both")
public void exportBothCsv(@RequestParam(name="cohort") String cohort, HttpServletResponse response) throws Exception {
    // 设置响应为ZIP类型
    response.setContentType("application/zip");
    response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"liabilities_bundle.zip\"");
    List<Report> data = reportsService.findByCohort(cohort);
    
    try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
        // 写入第一个CSV到ZIP包
        addCsvToZip(List.of("id", "amount"), "liabilities_id_amount.csv", data, zipOut);
        // 写入第二个CSV到ZIP包
        addCsvToZip(List.of("name", "currency"), "liabilities_name_currency.csv", data, zipOut);
    }
}

// 通用方法:生成CSV并写入ZIP压缩包
private void addCsvToZip(List<String> includeFields, String entryName, List<Report> data, ZipOutputStream zipOut) throws Exception {
    ZipEntry zipEntry = new ZipEntry(entryName);
    zipOut.putNextEntry(zipEntry);
    OutputStreamWriter writer = new OutputStreamWriter(zipOut, StandardCharsets.UTF_8);
    
    StatefulBeanToCsvBuilder<Report> builder = new StatefulBeanToCsvBuilder<Report>(writer)
            .withQuotechar(CSVWriter.NO_QUOTE_CHARACTER)
            .withSeparator(CSVWriter.DEFAULT_SEPARATOR)
            .withOrderedResults(true);
    Arrays.stream(Report.class.getDeclaredFields())
            .filter(field -> !includeFields.contains(field.getName()))
            .forEach(field -> builder.withIgnoreField(Report.class, field));
    
    builder.build().write(data);
    writer.flush();
    zipOut.closeEntry();
}

内容的提问来源于stack exchange,提问作者swarajsingh78

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 15:06:04