【问题标题】:Test maximum upload file size with MockMultipartFile使用 MockMultipartFile 测试最大上传文件大小
【发布时间】:2016-02-13 12:45:15
【问题描述】:

我使用 Spring Boot 创建了一个文件上传服务,并使用 Spring Mock Mvc 和 MockMultipartFile 对其进行了测试。我想测试超过最大文件大小时是否引发错误。以下测试失败,因为它收到 200。

RandomAccessFile f = new RandomAccessFile("t", "rw");
f.setLength(1024 * 1024 * 10);
InputStream is = Channels.newInputStream(f.getChannel());

MockMultipartFile firstFile = new MockMultipartFile("data", "file1.txt", "text/plain", is);

mvc.perform(fileUpload("/files")
    .file(firstFile))
    .andExpect(status().isInternalServerError());

有没有可能测试上传文件的大小?

【问题讨论】:

  • 您在哪里指定了最大上传大小?

标签: spring spring-mvc spring-boot spring-test spring-test-mvc


【解决方案1】:

根据documentation

如果 length 方法返回的文件的当前长度是 小于 newLength 参数,则文件将被扩展。在 在这种情况下,文件扩展部分的内容不是 已定义。

试试这个:

byte[] bytes = new byte[1024 * 1024 * 10];
MockMultipartFile firstFile = new MockMultipartFile("data", "file1.txt", "text/plain", bytes);

【讨论】:

  • 这对我不起作用。是否有可能使用 MockMVC 禁用文件大小检查?
  • @wuethrich44:看起来确实如此。我偶然发现了同样的问题,也找不到简单的解决方案。在不查看spring-web 的代码(或任何应该处理的代码)的情况下,我不禁注意到某些 Tomcat 代码(ie)抛出了实际的异常(超出限制) >java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$FileSizeLimitExceededException: The field file exceeds its maximum permitted size of 1048576 bytes.)。所以也许 Spring 只是将这个检查委托给底层应用程序服务器。
  • @wuethrich44:因为MockMvc 集成测试并没有真正包含实际的应用服务器...
  • @PriiduNeemre 好的,当 Tomcat 处理此错误时,似乎无法在 MockMvc 中进行测试。奇怪的是@David Jones 在这个解决方案上取得了成功......
  • David 可能正在做手动大小检查以及/而不是使用 Spring 属性。
【解决方案2】:

这里有同样的问题。

不知道是否有更好的解决方案,但我创建了一个注释来验证上传的图像列表,并在那里与其他人一起检查。

  • 验证图像的类
    public class ImageValidator implements ConstraintValidator<ValidImage, List<MultipartFile>> {

      @Override
      public boolean isValid(
          List<MultipartFile> listMultipartFile, ConstraintValidatorContext context) {

        for (var multipartFile : listMultipartFile) {
          var msgValidation = imageValidations(multipartFile);

          if (!msgValidation.isEmpty()) {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate(msgValidation).addConstraintViolation();
            return false;
          }
        }

        return true;
      }

      private String imageValidations(MultipartFile multipartFile) {
        var contentType = multipartFile.getContentType();

        if (!isSupportedContentType(contentType)) {
          return String.format(
              "Only JPG and PNG images are allowed, %s provided.", multipartFile.getContentType());
        } else if (multipartFile.isEmpty()) {
          return "It must not be an empty image.";
        } else if (multipartFile.getSize() > (1024 * 1024)) {
          return "File size should be at most 1MB";
        }

        return "";
      }

      private boolean isSupportedContentType(String contentType) {
        var supportedContents = List.of("image/jpg", "image/jpeg", "image/png");
        return supportedContents.contains(contentType);
      }
    }
  • 接口
    @Target(ElementType.PARAMETER)
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = {ImageValidator.class})
    public @interface ValidImage {
      String message() default "Invalid image file";

      Class<?>[] groups() default {};

      Class<? extends Payload>[] payload() default {};
    }

【讨论】:

    【解决方案3】:

    不能用 Spring 的 MockMultipartFile / MockMvc 测试这个。原因是错误的来源不是 Spring 本身,而是底层 Web 服务器(通常是 Tomcat),正如您在 MaxUploadSizeExceededException 的堆栈跟踪中看到的那样:

    org.springframework.web.multipart.MaxUploadSizeExceededException: Maximum upload size of 500000 bytes exceeded; nested exception is org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1065736) exceeds the configured maximum (500000)
        at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:160)
        at org.springframework.web.multipart.commons.CommonsMultipartResolver.resolveMultipart(CommonsMultipartResolver.java:139)
    [...]
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Unknown Source)
    Caused by: org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1065736) exceeds the configured maximum (500000)
        at org.apache.commons.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:965)
        at org.apache.commons.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:310)
        at org.apache.commons.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:334)
        at org.apache.commons.fileupload.servlet.ServletFileUpload.parseRequest(ServletFileUpload.java:115)
        at org.springframework.web.multipart.commons.CommonsMultipartResolver.parseRequest(CommonsMultipartResolver.java:156)
        ... 20 more
    

    当使用默认的 MockMvc 和 @SpringBootTest 及其默认设置时,没有启动真正的 Web 服务器,因此不会发生错误。

    但是,您可以通过提供@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 来告诉 Spring 为您的测试启动一个真正的 Web 服务器,这将(惊喜)在一个随机端口上启动一个 Web 服务器。您可以在测试类中使用@LocalServerPort 访问该端口。

    然后您可以编写一个测试,对您的测试服务器执行真实分段上传,而不是伪造的。 REST Assured 是一个库,除其他外,它可以做到这一点:

    @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
    public class MyTest {
    
        @LocalServerPort
        private int port;
    
        @Test
        void testMultipartUpload() throws Exception {
            File file = new File("my-file");
            RestAssured.baseURI = "http://localhost/api";
            RestAssured.port = port;
            Response res = given()
                    .multiPart("data", file, "text/plain")
                    .when().post("/upload");
            ...
        }
    
    }
    

    当您的上传过大时,此测试将显示服务器错误。

    【讨论】:

      猜你喜欢
      • 2015-08-04
      • 2014-04-04
      • 1970-01-01
      • 1970-01-01
      • 2013-06-23
      • 2014-11-19
      • 2020-07-07
      相关资源
      最近更新 更多