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

使用Spring WebClient下载文件遇空指针异常,BufferedInputStream可正常运行

WebClient代码的问题排查

1. 变量类型写错,直接触发NPE

你这段代码最致命的错误是变量类型声明和实际返回值不匹配:
Mono<byte[]> block = ...bodyToMono(byte[].class)).block()
这里block()方法是同步阻塞拿到Mono的最终结果,返回的是byte[]类型,根本不是Mono<byte[]>。这种错误会导致:

  • 如果请求返回空内容,block()会返回null,此时block变量(被错误声明成Mono类型)实际就是null,调用block.share()必然抛出NullPointerException。
  • 就算请求有内容,byte[]对象也没有share()方法,正常编译阶段就该报错,你能运行起来大概率是类型转换的问题,最终还是会炸NPE。

2. 重复调用block()纯属画蛇添足

就算修正了变量类型,block.share().block()也是完全多余的操作:
第一次block()已经同步拿到了文件的字节数组,没必要再把它当成Mono去调用share()和block()——Mono的block()只能触发一次订阅,重复调用的行为不可控,反而容易出问题。

3. 内存隐患(隐性问题)

用bodyToMono(byte[].class)会把整个文件一次性塞进内存,要是下载的文件很大(比如几百MB以上),很容易直接OOM。而BufferedInputStream是流式读取,不会一次性占满内存,这也是两种方式表现差别的隐性原因。

修正后的基础版代码

final WebClient client = WebClient.create();

// 修正变量类型,直接获取字节数组
byte[] fileBytes = client.get()
                         .uri("urlxxx")
                         .header("Authorization", "xxx")
                         .retrieve()
                         // 加上错误状态码处理,避免直接抛异常中断程序
                         .onStatus(HttpStatus::isError, response -> 
                             Mono.error(new RuntimeException("下载失败,状态码:" + response.statusCode()))
                         )
                         .bodyToMono(byte[].class)
                         .block();

// 写入文件,确保内容非空
Files.write(Paths.get("abc.tar.gz"), 
            Objects.requireNonNull(fileBytes, "下载的文件内容为空"), 
            StandardOpenOption.CREATE);

大文件友好的流式下载方案

如果是大文件,建议用流式处理,和BufferedInputStream的思路一致,避免内存溢出:

final WebClient client = WebClient.create();

client.get()
      .uri("urlxxx")
      .header("Authorization", "xxx")
      .retrieve()
      .onStatus(HttpStatus::isError, response -> 
          Mono.error(new RuntimeException("下载失败,状态码:" + response.statusCode()))
      )
      .bodyToFlux(DataBuffer.class)
      .map(dataBuffer -> {
          byte[] bytes = new byte[dataBuffer.readableByteCount()];
          dataBuffer.read(bytes);
          DataBufferUtils.release(dataBuffer);
          return bytes;
      })
      .collectList()
      .map(list -> {
          int totalLen = list.stream().mapToInt(arr -> arr.length).sum();
          byte[] result = new byte[totalLen];
          int pos = 0;
          for (byte[] arr : list) {
              System.arraycopy(arr, 0, result, pos, arr.length);
              pos += arr.length;
          }
          return result;
      })
      .subscribe(fileBytes -> {
          try {
              Files.write(Paths.get("abc.tar.gz"), fileBytes, StandardOpenOption.CREATE);
          } catch (IOException e) {
              throw new RuntimeException("写入文件失败", e);
          }
      });

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 03:00:54