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

Spring Boot实现CSV流式响应的问题(规避StreamingResponseBody)

实现Spring MVC流式返回CSV文本

可以实现类似JSON的流式返回效果,问题根源在于Spring默认的StringHttpMessageConverter不支持直接处理Stream<String>类型,而JSON的转换器MappingJackson2HttpMessageConverter原生支持Stream的流式序列化。下面提供两种可行方案:

方案一:自定义消息转换器(推荐)

自定义一个支持Stream<String>与text/plain类型的消息转换器,让Spring能够流式输出CSV内容:

1. 实现自定义转换器

import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.AbstractHttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.stream.Stream;

public class StreamStringHttpMessageConverter extends AbstractHttpMessageConverter<Stream<String>> {

    public StreamStringHttpMessageConverter() {
        super(MediaType.TEXT_PLAIN);
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        return Stream.class.isAssignableFrom(clazz);
    }

    @Override
    protected Stream<String> readInternal(Class<? extends Stream<String>> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
        // 无需处理请求输入,返回null即可
        return null;
    }

    @Override
    protected void writeInternal(Stream<String> stream, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
        // 流式写入每一行CSV内容,自动刷新输出流
        try (PrintWriter writer = new PrintWriter(outputMessage.getBody())) {
            stream.forEach(line -> {
                writer.println(line);
                writer.flush();
            });
        }
    }
}

2. 注册转换器到Spring容器

在Spring Boot中通过WebMvcConfigurer注册自定义转换器:

import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        converters.add(new StreamStringHttpMessageConverter());
    }
}

3. 修改控制器方法

保持原有返回Stream<String>的写法即可:

@GetMapping(value = "/csv", produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<Stream<String>> getCSVData() {
    // 替换为实际从数据库获取并转换为CSV行的Stream
    Stream<String> csvStream = Stream.of("id,name,age", "1,Alice,25", "2,Bob,30");
    return ResponseEntity.ok(csvStream);
}

方案二:转换为Iterable类型

利用Spring对Iterable类型的支持,无需自定义转换器,只需将Stream转换为Iterable:

@GetMapping(value = "/csv", produces = MediaType.TEXT_PLAIN_VALUE)
public ResponseEntity<Iterable<String>> getCSVData() {
    Stream<String> csvStream = Stream.of("id,name,age", "1,Alice,25", "2,Bob,30");
    // 将Stream转换为Iterable,Spring会自动遍历输出
    return ResponseEntity.ok(csvStream::iterator);
}

这种方式更简洁,但输出格式的可控性稍弱,默认会直接输出每个字符串内容,如需添加换行符需自行在CSV行中包含。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 01:40:21