使用 MockMultipartFile 测试最大上传文件大小

Posted

技术标签:

【中文标题】使用 MockMultipartFile 测试最大上传文件大小【英文标题】:Test maximum upload file size with 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());

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

【问题讨论】:

您在哪里指定了最大上传大小? 【参考方案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");
        ...
    


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

【讨论】:

以上是关于使用 MockMultipartFile 测试最大上传文件大小的主要内容,如果未能解决你的问题,请参考以下文章

MockMultipartFile mock文件上传

在训练数据上使用 MinMaxScaler 以生成用于测试数据的标准、最小值和最大值

最大限度。 Visual Studio 负载测试中的负载测试运行持续时间

针对最大深度绘制决策树训练/测试精度

您是不是对训练和测试数据分别应用最小最大缩放?

使用 Oracle 的 Spring Boot 应用程序 - ORA-01000:超出最大打开游标 - 负载测试期间发生错误