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

如何将文件以字符串形式发送至Spring Boot多部分REST API

二进制文件上传的编码异常问题分析与解决

问题场景

我有一个Spring Boot控制器处理multipart/form-data的文件上传,基于它生成了两个OpenAPI文件:一个包含format: binary(对应生成接收File参数的客户端),另一个移除了该字段(对应生成接收String参数的客户端)。

测试时发现:

  • 用File上传PDF文件,字节序列和原文件一致,传输正常;
  • 用String上传(将文件字节转成字符串),部分字节被篡改(比如原字节-100变成-17),且Content-Length远大于原文件,传输异常。

控制器代码

@RestController
@Tags(@Tag(name = "app"))
public class MyController {

    @Operation(
        requestBody = @RequestBody(
            content = @Content(
                mediaType = "multipart/form-data",
                schema = @Schema(type = "object"),
                schemaProperties = {
                    @SchemaProperty(name = "info", schema = @Schema(implementation = Info.class)),
                    @SchemaProperty(name = "file", schema = @Schema(type = "string", format = "binary"))
                }
            )
        )
    )
    @PostMapping(value = "/upload")
    ResponseEntity<Void> upload(HttpServletRequest request) throws Exception {
        UUID uuid = UUID.randomUUID();
        byte[] file = request.getPart("file").getInputStream().readAllBytes();
        System.out.printf("=================== %s ===========================%n", uuid);
        System.out.println(Arrays.toString(file).substring(0, 2750));
        Path path = Paths.get(".\\file_" + uuid + ".pdf");
        try { Files.write(path, file);}
        catch (IOException exception) { throw new RuntimeException(exception); }
        return ResponseEntity.ok().build();
    }
    
}

OpenAPI定义差异

  • 正常版本(生成File类型客户端):
file:
  type: string
  format: binary
  • 异常版本(生成String类型客户端):
file:
  type: string

测试代码

File originalPdf;
try { originalPdf = new ClassPathResource("Original.pdf").getFile(); }
catch (IOException exception) { throw new RuntimeException(exception); }
try { appApi.upload(new Info(), originalPdf); }
catch (ApiException exception) { throw new RuntimeException(exception); }

byte[] byteArray = new byte[(int) originalPdf.length()];
try (FileInputStream inputStream = new FileInputStream(originalPdf)) { inputStream.read(byteArray);}
catch (Exception exception) { throw new RuntimeException(exception); }
try { appApiString.upload(new Info(), new String(byteArray)); }
catch (ApiException exception) { throw new RuntimeException(exception); }

客户端日志对比

  • File形式上传日志:
Content-Disposition: form-data; name="file"; filename="Original.pdf"
Content-Type: application/pdf
Content-Length: 51375
  • String形式上传日志:
Content-Disposition: form-data; name="file"
Content-Type: text/plain; charset=utf-8
Content-Length: 87293

问题原因

  1. 二进制与字符串的本质冲突:PDF是二进制文件,其字节序列并非合法的文本编码(如UTF-8)。将二进制字节直接转为字符串时,JVM会用指定编码(默认UTF-8)解析,遇到无法识别的字节会替换成替换字符(UTF-8中为0xFFFD,对应十进制-17),这就是你看到字节篡改的直接原因。
  2. 客户端处理逻辑差异:
    • 带format: binary的OpenAPI定义会让生成器识别这是二进制文件,客户端会以字节流形式上传,保留原始数据,Content-Type设为对应文件类型,Content-Length与原文件一致。
    • 仅type: string的定义会让生成器认为这是普通文本,客户端会将字符串以UTF-8编码转成字节上传,既篡改了原始二进制数据,又因编码转换导致字节数增多(单个无法识别的字节可能被转成多个字节的替换字符),所以Content-Length变大。

解决办法

  1. 保留OpenAPI中的format: binary:这是最优方案,确保生成的客户端用File或字节流类型处理二进制文件,完全避免字符串转换带来的数据损坏。
  2. 若必须使用String参数(不推荐):
    • 将二进制数据做Base64编码后转为字符串上传;
    • 在OpenAPI中为file字段添加format: base64,让生成器识别这是Base64编码的二进制数据,客户端会自动处理编码解码逻辑;
    • 服务端对应修改,将接收的Base64字符串解码回字节数组。

注意:Base64编码会让数据体积增加约30%,大文件场景不建议使用,优先选择format: binary方案。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 21:57:07