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

Spring Boot MVC应用中用Flux实现PDF下载端点的问题

问题解决:Spring MVC应用中流式返回PDF(避免内存缓冲)

核心结论

在基于Tomcat的Spring Boot MVC应用中,可以实现流式返回PDF(不缓冲整个文件到内存),但不能直接返回Flux<DataBuffer>——因为MVC的消息转换器无法直接处理这种响应式数据流形式,需要改用Spring MVC原生支持的StreamingResponseBody来实现流式输出。

错误原因分析

你遇到的HttpMessageNotWritableException,本质是MVC框架对响应式类型的处理逻辑导致的:MVC默认会将Flux视为普通集合类,尝试把它收集成CollectedValuesList,然后寻找对应的消息转换器,而不存在能处理这种类型+PDF contentType的转换器,因此报错。

解决方案:使用StreamingResponseBody实现流式传输

StreamingResponseBody是Spring MVC专门用于流式响应的接口,它允许直接将数据写入Servlet的OutputStream,完全符合你“避免内存缓冲”的需求。结合WebClient的流式获取能力,可以实现从内部服务器流式读取PDF,再流式输出给客户端的端到端无缓冲传输。

修正后的控制器代码

@GetMapping(path = "/{id}/download-flux", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<StreamingResponseBody> downloadPdf(@PathVariable final int id) {
    // 从数据库获取PDF路径
    Report report = reportService.getReport(id);

    // 定义流式响应体
    StreamingResponseBody responseBody = outputStream -> {
        webClient.get()
                .uri("/" + report.getPath())
                .accept(MediaType.APPLICATION_PDF)
                .retrieve()
                // 流式获取内部服务器返回的PDF数据
                .bodyToFlux(DataBuffer.class)
                .doOnNext(dataBuffer -> {
                    try {
                        // 将DataBuffer中的数据写入输出流
                        ByteBuffer byteBuffer = dataBuffer.asByteBuffer();
                        byte[] bytes = new byte[byteBuffer.remaining()];
                        byteBuffer.get(bytes);
                        outputStream.write(bytes);
                        // 释放DataBuffer资源(池化对象,必须手动释放避免内存泄漏)
                        DataBufferUtils.release(dataBuffer);
                    } catch (IOException e) {
                        throw new RuntimeException("流式写入PDF失败", e);
                    }
                })
                // 等待所有数据处理完成
                .blockLast();
    };

    return ResponseEntity.ok()
            .contentType(MediaType.APPLICATION_PDF)
            .body(responseBody);
}

关键注意事项

  1. DataBuffer资源释放:DataBuffer是池化对象,使用后必须调用DataBufferUtils.release()释放,否则会导致内存泄漏。
  2. WebClient的流式能力:bodyToFlux(DataBuffer.class)确保从内部服务器读取数据时是流式的,不会将整个PDF加载到内存。
  3. 依赖兼容性:你引入的spring-boot-starter-webflux依赖可以在MVC应用中正常使用,WebClient本身支持在非WebFlux环境下运行,无需切换到WebFlux服务器。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 06:00:21