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

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");
    }
}

错误原因

  1. MockMultipartFile缺少有效文件名:创建MockMultipartFile时只传了参数名和字节数组,未指定原始文件名,导致multipartFile.getOriginalFilename()返回null。后续用空字符串创建File对象,触发FileNotFoundException。
  2. 测试中冗余的实际文件转换:测试的核心是mockimageStoreService的行为,不需要执行真实的文件转换逻辑,直接调用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 11:45:34