如何将文件以字符串形式发送至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
问题原因
- 二进制与字符串的本质冲突:PDF是二进制文件,其字节序列并非合法的文本编码(如UTF-8)。将二进制字节直接转为字符串时,JVM会用指定编码(默认UTF-8)解析,遇到无法识别的字节会替换成替换字符(UTF-8中为
0xFFFD,对应十进制-17),这就是你看到字节篡改的直接原因。 - 客户端处理逻辑差异:
- 带
format: binary的OpenAPI定义会让生成器识别这是二进制文件,客户端会以字节流形式上传,保留原始数据,Content-Type设为对应文件类型,Content-Length与原文件一致。 - 仅
type: string的定义会让生成器认为这是普通文本,客户端会将字符串以UTF-8编码转成字节上传,既篡改了原始二进制数据,又因编码转换导致字节数增多(单个无法识别的字节可能被转成多个字节的替换字符),所以Content-Length变大。
- 带
解决办法
- 保留OpenAPI中的
format: binary:这是最优方案,确保生成的客户端用File或字节流类型处理二进制文件,完全避免字符串转换带来的数据损坏。 - 若必须使用String参数(不推荐):
- 将二进制数据做Base64编码后转为字符串上传;
- 在OpenAPI中为
file字段添加format: base64,让生成器识别这是Base64编码的二进制数据,客户端会自动处理编码解码逻辑; - 服务端对应修改,将接收的Base64字符串解码回字节数组。
注意:Base64编码会让数据体积增加约30%,大文件场景不建议使用,优先选择format: binary方案。
内容的提问来源于stack exchange,提问作者tweetysat
相关产品推荐
相关产品推荐

