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

从服务器下载1GB大文件时请求超时的解决方案咨询

你好!我来帮你解决这两个困扰你一周的问题——大文件下载时ServiceB的异步超时,以及直接从ServiceA下载时没有加载指示器的问题。咱们一步步来:

一、解决ServiceB的AsyncRequestTimeoutException(大文件下载超时)

这个异常的核心原因是Spring MVC默认的异步请求超时时间太短,没法覆盖大文件的传输时长。同时你的WebClient用法也有可以优化的地方,具体调整如下:

  1. 延长Spring MVC异步请求超时时间
    默认情况下,Spring MVC的异步请求超时只有10秒左右,完全不够大文件传输。你可以通过两种方式修改:
  • 配置文件方式(application.properties):
spring.mvc.async.request-timeout=3600000  # 设置为1小时,可根据你的最大文件大小调整
  • Java配置类方式:
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
        // 设置默认超时时间为1小时
        configurer.setDefaultTimeout(3600000);
    }
}
  1. 给WebClient设置专属超时
    除了Spring MVC的全局超时,还要给WebClient本身配置连接和读取超时,避免ServiceA端传输缓慢导致WebClient先触发超时:
// 创建自定义HttpClient,设置超时
HttpClient httpClient = HttpClient.create()
        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000) // 连接超时5秒
        .responseTimeout(Duration.ofHours(1)); // 读取超时1小时

// 用自定义HttpClient构建WebClient
WebClient client = WebClient.builder()
        .clientConnector(new ReactorClientHttpConnector(httpClient))
        .baseUrl(STORAGE_HOST)
        .build();
  1. 去掉block(),用反应式方式返回
    你当前使用block()会阻塞线程,既浪费了反应式编程的优势,也容易引发线程池耗尽和超时问题。建议在ServiceB的控制器中直接返回反应式类型,让Spring MVC自动处理异步流:
@GetMapping("/public/download")
public Mono<Void> downloadPublicFile(@RequestParam String requestId, HttpServletResponse response) {
    // 构建带超时配置的WebClient
    HttpClient httpClient = HttpClient.create().responseTimeout(Duration.ofHours(1));
    WebClient client = WebClient.builder()
            .clientConnector(new ReactorClientHttpConnector(httpClient))
            .baseUrl(STORAGE_HOST)
            .build();

    // 获取ServiceA的文件流
    Flux<DataBuffer> fileDataStream = client.get()
            .uri(uriBuilder -> uriBuilder.path("folders/zip/download")
                    .queryParam("requestId", requestId)
                    .build())
            .accept(MediaType.APPLICATION_OCTET_STREAM)
            .retrieve()
            .bodyToFlux(DataBuffer.class);

    // 从ServiceA的响应头中获取文件信息,设置到客户端响应中
    client.get()
            .uri(uriBuilder -> uriBuilder.path("folders/zip/download")
                    .queryParam("requestId", requestId)
                    .build())
            .head()
            .subscribe(headers -> {
                response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
                response.setContentLength(headers.getContentLength());
                response.setHeader(HttpHeaders.ETAG, headers.getETag());
            });

    // 流式写入响应输出流,返回Mono<Void>让Spring MVC处理异步
    return DataBufferUtils.write(fileDataStream, response.getOutputStream())
            .map(DataBufferUtils::release)
            .then();
}

这样修改后,ServiceB会以完全异步的方式流式传输文件,避免阻塞线程,也能有效解决超时问题。

二、解决ServiceA下载无加载指示器的问题

直接从ServiceA下载时没有加载指示器,是因为InputStreamResource的处理方式无法让浏览器正确识别下载进度。建议改用StreamingResponseBody,它是Spring专门为流式输出设计的类型,能让浏览器根据你设置的Content-Length计算并显示下载进度:

修改ServiceA的返回代码:

File zipFile = new File(folderDownload.getZipPath());
try {
    // 创建StreamingResponseBody,逐块写入文件内容
    StreamingResponseBody responseBody = outputStream -> {
        try (InputStream inputStream = new BufferedInputStream(new FileInputStream(zipFile))) {
            byte[] buffer = new byte[8192]; // 8KB缓冲区,可调整
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
                // 强制刷新输出流,确保内容及时发送到浏览器
                outputStream.flush();
            }
        }
    };

    return ResponseEntity
            .status(HttpStatus.OK)
            .eTag(folderDownload.getDownloadId())
            .contentLength(zipFile.length())
            .lastModified(Instant.ofEpochMilli(zipFile.lastModified()))
            .body(responseBody);
} catch (Exception e) {
    throw new DownloadException(zipFile.getName(), e.getMessage(), 400);
}

StreamingResponseBody会逐块输出文件,结合你已经设置的Content-Length,浏览器就能准确计算下载进度,显示加载指示器了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 10:02:48