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
相关产品推荐
相关产品推荐

