Spring Boot传递MultipartFile至服务器的序列化问题求助
在Spring Boot项目中,尝试通过服务器将文件存入数据库,选择用MultipartFile传递文件,但遇到序列化错误:
Caused by: jakarta.ws.rs.client.ResponseProcessingException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.mock.web.MockMultipartFile["inputStream"])
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.ByteArrayInputStream and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.mock.web.MockMultipartFile["inputStream"])
已在application.properties中添加spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false,但问题依旧。Gradle构建已成功,Postman测试上传功能正常,但客户端代码调用失败。
相关代码如下:
客户端ServerUtils代码
public void saveFile(File file) throws IOException { internalPostRequest("secure/" + store.accessStore().getUsername() + "/file/add", Entity.entity(convert(file), APPLICATION_JSON), new GenericType<>(){}); } public static MultipartFile convert(File file) throws IOException { String filename = file.getName(); FileInputStream inputStream = new FileInputStream(file); return new MockMultipartFile(filename, inputStream); } private <T> T internalPostRequest(String path, Entity send, GenericType<T> retType) { return ClientBuilder.newClient(new ClientConfig()) .target(getServer()).path(path) .request(APPLICATION_JSON) .accept(APPLICATION_JSON) .post(send, retType); }
服务端FileController代码
public class FileController { @Autowired private StorageService service; @PostMapping("/add") public ResponseEntity<?> uploadFile(@RequestParam("file") MultipartFile file) throws IOException { String uploadFile = service.uploadFile(file); return ResponseEntity.status(HttpStatus.OK) .body(uploadFile); } }
服务端StorageService代码
@Service public class StorageService { @Autowired private StorageRepository repository; public String uploadFile(MultipartFile file) throws IOException { repository.save(new FileData(file.getOriginalFilename(), file.getContentType(), compressFile(file.getBytes()))); return "File uploaded succesfully: "+ file.getOriginalFilename(); } public String getType(String fileName){ Optional<FileData> dbFileData = repository.findByName(fileName); if (dbFileData.isPresent()) { return dbFileData.get().getType(); } else { return "File not found: " + fileName; } } public static byte[] compressFile(byte[] data) { Deflater deflater = new Deflater(); deflater.setLevel(Deflater.BEST_COMPRESSION); deflater.setInput(data); deflater.finish(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); byte[] tmp = new byte[4*1024]; while (!deflater.finished()) { int size = deflater.deflate(tmp); outputStream.write(tmp, 0, size); } try { outputStream.close(); } catch (Exception ignored) { } return outputStream.toByteArray(); } }
核心问题分析
客户端错误地将MultipartFile以APPLICATION_JSON格式发送,Jackson尝试序列化MockMultipartFile内部的ByteArrayInputStream,而该类没有可序列化的属性,导致报错。Postman正常是因为它用的是multipart/form-data格式,符合服务端@RequestParam("file")的接收要求。
修复步骤
客户端改用multipart/form-data格式发送请求
不要直接序列化MultipartFile,而是构建multipart表单数据。修改客户端saveFile和internalPostRequest方法:public void saveFile(File file) throws IOException { // 构建multipart表单 FormDataMultiPart formData = new FormDataMultiPart(); // 将文件转为InputStream,添加到表单中 try (FileInputStream fis = new FileInputStream(file)) { formData.field("file", fis, MediaType.MULTIPART_FORM_DATA_TYPE, file.getName()); internalPostRequest("secure/" + store.accessStore().getUsername() + "/file/add", Entity.entity(formData, MediaType.MULTIPART_FORM_DATA_TYPE), new GenericType<>(){}); } } private <T> T internalPostRequest(String path, Entity send, GenericType<T> retType) { return ClientBuilder.newClient(new ClientConfig()) .target(getServer()).path(path) .request(MediaType.APPLICATION_JSON) // 接收JSON响应 .post(send, retType); }移除无效配置(可选)
之前添加的spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false对这个问题无效,可以从application.properties中删除,因为问题根源不是空Bean序列化,而是错误的请求格式。优化文件转换逻辑(可选)
客户端不需要创建MockMultipartFile对象,直接用文件流构建multipart表单即可,可移除原有的convert方法。
验证
修改后重新调用客户端的saveFile方法,即可正常上传文件,不会再出现序列化错误。
内容的提问来源于stack exchange,提问作者Micu Bogdan

