Spring Boot中Amazon S3图片上传JUnit测试报错修复咨询
Spring Boot S3图片上传JUnit测试异常修复
问题描述
在Spring Boot项目编写Amazon S3图片上传的JUnit测试时,执行测试抛出异常:
Failed to convert multipartFile to File (java.lang.RuntimeException: Failed to convert multipartFile to Filejava.io.FileNotFoundException: )
相关代码如下:
Rest控制器方法
@RestController @RequestMapping("/api/v1/bookImage") @RequiredArgsConstructor public class ImageRestController { @PostMapping public ResponseEntity<String> uploadImage(@RequestParam("bookId") Long bookId, @RequestParam("file") MultipartFile file) { final String uploadImg = imageStoreService.uploadImg(convert(file), bookId); service.saveImage(bookId, uploadImg); return ResponseEntity.ok(uploadImg); } private File convert(final MultipartFile multipartFile) { // convert multipartFile to File File file = new File(Objects.requireNonNull(multipartFile.getOriginalFilename())); try (FileOutputStream fos = new FileOutputStream(file)) { fos.write(multipartFile.getBytes()); return file; } catch (IOException e) { throw new RuntimeException("Failed to convert multipartFile to File"); } } }
ImageService方法
public String uploadImg(File file, Long bookId) { s3amazon.putObject(BUCKET_NAME, bookId.toString(), file); return baseUrl + bookId; }
测试方法
@Test void itShouldGetImagePath_WhenValidBookIdAndFile() throws Exception{ // given - precondition or setup String bookId = "1"; String imagePath = "amazon-imagepath"; String baseUrl = String.format(imagePath + "/%s", bookId); Long bookIdValue = 1L; MockMultipartFile uploadFile = new MockMultipartFile("file", new byte[1]); // when - action or the behaviour that we are going test when(imageStoreService.uploadImg(convert(uploadFile), bookIdValue)).thenReturn(baseUrl); // 此处报错 // then - verify the output mvc.perform(MockMvcRequestBuilders.multipart("/api/v1/bookImage") .file(uploadFile) .param("bookId", bookId)) .andExpect((ResultMatcher) content().string(baseUrl)) .andExpect(status().isOk()); } private File convert(final MultipartFile multipartFile) { // convert multipartFile to File File file = new File(Objects.requireNonNull(multipartFile.getOriginalFilename())); try (FileOutputStream fos = new FileOutputStream(file)) { fos.write(multipartFile.getBytes()); return file; } catch (IOException e) { throw new RuntimeException("Failed to convert multipartFile to File"); } }
错误原因
- MockMultipartFile缺少有效文件名:创建
MockMultipartFile时只传了参数名和字节数组,未指定原始文件名,导致multipartFile.getOriginalFilename()返回null。后续用空字符串创建File对象,触发FileNotFoundException。 - 测试中冗余的实际文件转换:测试的核心是mock
imageStoreService的行为,不需要执行真实的文件转换逻辑,直接调用convert(uploadFile)既会生成无用文件,又会触发上述异常。
修复方案
1. 修正MockMultipartFile的创建
创建MockMultipartFile时传入完整参数,确保原始文件名非空:
MockMultipartFile uploadFile = new MockMultipartFile( "file", // 请求参数名 "test-book.jpg", // 原始文件名,必须非空 "image/jpeg", // 文件MIME类型 new byte[1] // 文件字节内容 );
2. 改用Mockito参数匹配器mock方法调用
不需要生成实际的File对象,用参数匹配器匹配方法参数即可,避免执行真实的文件转换:
// 替换原来的when语句,用any()匹配任意File对象,anyLong()匹配任意Long类型的bookId when(imageStoreService.uploadImg(any(File.class), anyLong())).thenReturn(baseUrl);
3. (可选)优化生产代码的文件转换逻辑
生产代码中直接用原始文件名创建File存在文件名冲突、权限不足的风险,建议改用临时文件:
import org.apache.commons.io.FilenameUtils; private File convert(final MultipartFile multipartFile) { try { // 创建临时文件,自动生成唯一文件名 String extension = FilenameUtils.getExtension(multipartFile.getOriginalFilename()); File tempFile = File.createTempFile("book-img-", "." + extension); // 直接将MultipartFile内容写入临时文件,比手动用FileOutputStream更简洁 multipartFile.transferTo(tempFile); // 标记JVM退出时自动删除临时文件 tempFile.deleteOnExit(); return tempFile; } catch (IOException e) { // 异常中携带原始异常信息,方便排查问题 throw new RuntimeException("Failed to convert multipartFile to File", e); } }
注:需要引入Apache Commons IO依赖,或者自行实现文件扩展名提取逻辑。
内容的提问来源于stack exchange,提问作者Sercan Noyan Germiyanoğlu
相关产品推荐
相关产品推荐

