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

使用Spring WebClient以InputStream上传文件遇500内部服务器错误

问题场景

我在基于WebFlux的SpringBoot应用中开发了一个接收FilePart作为输入的API。为了用Tika库做文件类型校验,我先把FilePart转换成了InputStream,但用WebClient将这个流上传到目标API时,收到了500内部服务器错误。如果跳过转流步骤,直接用FilePart上传,WebClient就能正常工作。

出错代码

MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("file", new InputStreamResource(inputStream)).header("Content-Disposition",
    "form-data; name=file; filename=\"test.txt\"");
return webClient
        .post()
        .uri("https://example.com/file")
        .contentType(MediaType.MULTIPART_FORM_DATA)
        .body(BodyInserters.fromMultipartData(builder.build()))
        .retrieve()
        .bodyToMono(String.class);

正常工作的代码

return webClient.post()
                .uri("https://example.com/file")
                .contentType(MediaType.MULTIPART_FORM_DATA)
                .body(BodyInserters.fromMultipartData("file", filePart))
                .retrieve()
                .bodyToMono(String.class);
问题原因
  1. InputStream只能读取一次:InputStreamResource包装的流是一次性的,WebFlux在异步处理过程中可能会多次尝试读取流,导致流耗尽或关闭,目标服务器无法正确解析文件内容。
  2. 缺少必要元数据:直接使用InputStreamResource时,没有提供文件的Content-Type和内容长度,目标服务器无法识别文件类型或校验文件完整性,从而抛出500错误。
  3. Header格式问题:手动设置Content-Disposition时的引号转义有误,导致表单数据解析失败。
解决方案

将InputStream转换为可重复读取的ByteArrayResource,同时正确设置文件元数据:

// 先将FilePart内容读取到字节数组(用于Tika校验和后续上传)
byte[] fileBytes = filePart.content()
        .map(DataBuffer::asByteBuffer)
        .map(ByteBuffer::array)
        .reduce(new byte[0], (a, b) -> {
            byte[] result = new byte[a.length + b.length];
            System.arraycopy(a, 0, result, 0, a.length);
            System.arraycopy(b, 0, result, a.length, b.length);
            return result;
        })
        .block();

// Tika校验文件类型
Tika tika = new Tika();
String detectedMediaType = tika.detect(fileBytes);

// 构建Multipart请求
MultipartBodyBuilder builder = new MultipartBodyBuilder();
builder.part("file", new ByteArrayResource(fileBytes))
        .header("Content-Disposition", String.format("form-data; name=\"file\"; filename=\"%s\"", filePart.filename()))
        .header("Content-Type", detectedMediaType);

return webClient
        .post()
        .uri("https://example.com/file")
        .contentType(MediaType.MULTIPART_FORM_DATA)
        .body(BodyInserters.fromMultipartData(builder.build()))
        .retrieve()
        .bodyToMono(String.class);

关键改进点

  • 用ByteArrayResource替代InputStreamResource,支持重复读取流,适配WebFlux异步处理模型。
  • 保留原FilePart的文件名,通过Tika自动识别并设置正确的Content-Type。
  • 修正Content-Disposition头的引号格式,确保表单数据解析正确。

内容的提问来源于stack exchange,提问作者Prajwal Raju P

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 08:40:13