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

如何在Java API中同时返回CSV文件与分页数据?

实现方案

由于HTTP响应只能返回单一类型的内容(要么JSON要么文件),所以需要拆分接口来实现需求:一个接口返回分页元数据和CSV下载链接,另一个接口负责生成对应分页的CSV文件并提供下载。

1. 定义分页响应DTO

先创建一个用于封装分页信息和下载链接的实体类:

public class PagedCsvResponse {
    private long totalItems;
    private int currentPage;
    private int totalPages;
    private String downloadUrl;

    // 构造方法、getter/setter
    public PagedCsvResponse(long totalItems, int currentPage, int totalPages, String downloadUrl) {
        this.totalItems = totalItems;
        this.currentPage = currentPage;
        this.totalPages = totalPages;
        this.downloadUrl = downloadUrl;
    }

    // 省略getter和setter方法
}

2. 实现分页数据查询接口

这个接口负责查询分页元数据,并生成对应分页的CSV下载链接:

@RestController
@RequestMapping("/api/data")
public class DataController {

    @Autowired
    private DataRepository dataRepository; // 你的数据访问层

    @GetMapping("/paged-meta")
    public ResponseEntity<PagedCsvResponse> getPagedDataMeta(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "10") int size,
            HttpServletRequest request) {

        // 执行分页查询
        Page<DataEntity> dataPage = dataRepository.findAll(PageRequest.of(page, size));

        // 生成完整下载链接:基于当前请求上下文拼接域名、接口路径和分页参数
        String baseUrl = String.format("%s://%s:%d%s",
                request.getScheme(),
                request.getServerName(),
                request.getServerPort(),
                request.getContextPath());
        String downloadUrl = baseUrl + "/api/data/csv?page=" + page + "&size=" + size;

        // 封装响应内容
        PagedCsvResponse response = new PagedCsvResponse(
                dataPage.getTotalElements(),
                dataPage.getNumber(),
                dataPage.getTotalPages(),
                downloadUrl
        );

        return ResponseEntity.ok(response);
    }
}

3. 实现CSV文件下载接口

这个接口根据传入的分页参数,生成对应页的CSV文件并返回给客户端:

@GetMapping("/csv")
public ResponseEntity<InputStreamResource> downloadPagedCsv(
        @RequestParam(defaultValue = "0") int page,
        @RequestParam(defaultValue = "10") int size) throws IOException {

    // 查询当前页的数据
    Page<DataEntity> dataPage = dataRepository.findAll(PageRequest.of(page, size));

    // 内存中生成CSV内容(若数据量较大,建议改用流式写入避免内存溢出)
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    CSVWriter csvWriter = new CSVWriter(new OutputStreamWriter(outputStream));

    // 写入CSV表头(替换为你的实际字段)
    csvWriter.writeNext(new String[]{"ID", "名称", "创建时间"});

    // 写入数据行
    for (DataEntity entity : dataPage.getContent()) {
        csvWriter.writeNext(new String[]{
                String.valueOf(entity.getId()),
                entity.getName(),
                entity.getCreateTime().toString()
        });
    }

    csvWriter.close();
    outputStream.close();

    // 转换为InputStreamResource供响应返回
    InputStreamResource resource = new InputStreamResource(new ByteArrayInputStream(outputStream.toByteArray()));

    // 设置响应头,指定带分页标识的文件名
    String filename = String.format("data-page-%d.csv", page);
    HttpHeaders headers = new HttpHeaders();
    headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + filename);

    return ResponseEntity.ok()
            .headers(headers)
            .contentType(MediaType.parseMediaType("text/csv"))
            .body(resource);
}

4. 额外优化建议

  • 如果需要支持全量数据下载,可以在分页元数据接口中额外添加全量下载链接,此时下载接口需要循环查询所有分页数据并拼接成完整CSV(建议用StreamingResponseBody流式输出,避免内存溢出)
  • 若接口需要权限验证,可在下载链接中携带认证参数(比如JWT令牌),确保只有授权用户能访问下载接口
  • 前端拿到分页响应后,可直接将downloadUrl渲染为可点击的<a>标签,用户点击后即可触发浏览器下载对应CSV文件

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:18:12