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

Spring Boot大文件流式下载内存溢出及Swagger UI异常求助

解决方案:大文件流式下载OOM与Swagger渲染问题

一、解决Direct Buffer内存不足导致的OutOfMemoryError

问题原因

当前WebClient请求未启用流式消费,默认会将响应体全量缓冲到Direct Buffer中,当文件超过JVM的Direct Memory上限时触发OOM;同时未正确释放DataBuffer会加剧内存泄漏。

修复步骤

  1. 流式消费WebClient响应体
    使用bodyToFlux(DataBuffer.class)将响应拆分为数据流,避免一次性加载整个文件到内存,同时手动释放每个DataBuffer防止泄漏:

    @GetMapping("/download")
    public Mono<Void> downloadLargeFile(HttpServletResponse response) {
        // 设置响应头,告知客户端为二进制文件
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=large-file.bin");
        
        ServletServerHttpResponse servletResponse = new ServletServerHttpResponse(response);
        
        return webClient.get()
                .uri("http://remote-server/large-file")
                .retrieve()
                .bodyToFlux(DataBuffer.class)
                .concatMap(dataBuffer -> {
                    try {
                        // 分块写入响应输出流
                        servletResponse.getBody().write(dataBuffer.asByteBuffer());
                        return Mono.just(dataBuffer);
                    } catch (IOException e) {
                        return Mono.error(new RuntimeException("写入响应失败", e));
                    }
                })
                .doOnNext(DataBufferUtils::release) // 释放每个DataBuffer
                .then()
                .doFinally(signal -> {
                    try {
                        servletResponse.getBody().flush();
                        servletResponse.getBody().close();
                    } catch (IOException e) {
                        // 记录流关闭异常
                    }
                });
    }
    
  2. 优化WebClient的Netty配置
    调整Netty缓冲区策略,使用池化分配器并限制接收缓冲区大小,减少Direct Buffer占用:

    @Bean
    public WebClient webClient() {
        HttpClient httpClient = HttpClient.create()
                .option(ChannelOption.SO_RCVBUF, 1024 * 1024) // 设置接收缓冲区为1MB
                .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT) // 使用池化分配器
                .responseTimeout(Duration.ofMinutes(5)); // 延长超时适配大文件下载
        
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient))
                .build();
    }
    
  3. 临时调整JVM参数(可选)
    若仍有内存压力,可临时增大Direct Memory上限:

    -XX:MaxDirectMemorySize=128m
    

    注:此为治标方案,核心仍需依赖流式处理解决根本问题。

二、解决Swagger UI响应渲染异常

问题原因

Swagger UI无法识别未明确声明的二进制响应类型,默认将其当作文本渲染,导致提示“Unrecognized response type”。

修复步骤

  1. 明确声明响应的媒体类型与格式
    通过OpenAPI注解告诉Swagger该接口返回二进制流,以正确渲染响应:

    基于SpringDoc(Spring Boot 3+推荐)

    @GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    @Operation(summary = "下载大文件", responses = {
            @ApiResponse(responseCode = "200", description = "文件下载成功",
                    content = @Content(mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE,
                            schema = @Schema(type = "string", format = "binary")))
    })
    public Mono<Void> downloadLargeFile(HttpServletResponse response) {
        // 方法体同上
    }
    

    基于Springfox(旧版本Spring Boot)

    @GetMapping(value = "/download", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
    @ApiOperation(value = "下载大文件")
    @ApiResponses(value = {
            @ApiResponse(code = 200, message = "文件下载成功",
                    content = @Content(mediaType = MediaType.APPLICATION_OCTET_STREAM_VALUE,
                            schema = @Schema(type = "string", format = "binary")))
    })
    public Mono<Void> downloadLargeFile(HttpServletResponse response) {
        // 方法体同上
    }
    
  2. 确保响应头正确设置
    控制器中必须设置Content-Type为application/octet-stream,Swagger会读取该头信息匹配注解配置,从而正确识别响应类型。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 03:52:49