Undertow 上传多部分文件超过设置值时抛出 RuntimeException

Posted

技术标签:

【中文标题】Undertow 上传多部分文件超过设置值时抛出 RuntimeException【英文标题】:Undertow throws RuntimeException when uploading multipart file exceeding the setting value 【发布时间】:2018-04-08 15:35:08 【问题描述】:

我正在Spring Guide 运行完整版本的 Spring boot Upload 文件指南,但我使用 Undertow 作为嵌入式 servlet 而不是 Tomcat 默认值。它奏效了。

当我尝试上传大小大于配置文件中值的文件时

spring.http.multipart.max-file-size=128KB
spring.http.multipart.max-request-size=128KB

它引发异常。这是预期的行为

使用Tomcat内嵌的servlet,可以通过捕捉SizeLimitExceededExceptionMultipartException轻松处理

但是使用 Undertow,它会引发 RuntimeException 事件,我在全局处理程序中捕获它:

@ControllerAdvice
public class GlobalControllerExceptionHandler extends ResponseEntityExceptionHandler 
    @ExceptionHandler(RuntimeException.class, Exception.class, IOException.class)
    ResponseEntity<?> handleControllerException(Exception exc, HttpServletRequest request, Throwable ex) 
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
    

异常消息:

java.lang.RuntimeException: java.io.IOException: UT000020: Connection terminated as request was larger than 131072
    at io.undertow.servlet.spec.HttpServletRequestImpl.parseFormData(HttpServletRequestImpl.java:779) ~[undertow-servlet-1.4.20.Final.jar:1.4.20.Final]
    at io.undertow.servlet.spec.HttpServletRequestImpl.getParameter(HttpServletRequestImpl.java:653) ~[undertow-servlet-1.4.20.Final.jar:1.4.20.Final]
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:75) ~[spring-web-4.3.12.RELEASE.jar:4.3.12.RELEASE]
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-4.3.12.RELEASE.jar:4.3.12.RELEASE]

代码如下:

控制器

@PostMapping("/")
public String handleFileUpload(@RequestParam("file") MultipartFile file,
        RedirectAttributes redirectAttributes) 

    storageService.store(file);
    redirectAttributes.addFlashAttribute("message",
            "You successfully uploaded " + file.getOriginalFilename() + "!");

    return "redirect:/";

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.springframework</groupId>
    <artifactId>gs-uploading-files</artifactId>
    <version>0.1.0</version>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.8.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <version>1.3.0.RELEASE</version>
        </dependency>
    </dependencies>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

有人跟我有同样的问题吗?超过最大文件大小上传时如何捕获异常?

任何帮助将不胜感激

【问题讨论】:

对我来说(可能在 spring-boot 中)它看起来像一个错误,即违反 spring.http.multipart.max-file-size 的异常可以使用 ExceptionHandler 与一个容器而不是其他...... . 看起来 spring-boot 正确地将 spring 属性映射到 servlet 规范属性,并且限制正确适用于 tomcat 和 undertow。 HttpServletRequest.getParts() 契约定义了当达到 maxFileSize 或 maxRequestSize 时应该抛出一个 IllegalStateException,它被 spring 包装在一个 MultipartException 中。这个接触后面没有undertow,导致在不同的点抛出错误,spring不处理,所以异常从框架中逃逸出来,不被任何定义的@ExceptionHandler方法处理。 我还提出了关于 undertow jira 的问题。你可以在这里关注它:issues.jboss.org/browse/UNDERTOW-1227 我遇到了同样的问题。看起来@YannisSermetziadis 提出的问题已在1.4.22.Final 中得到解决,但是在升级 Spring Boot 和 Undertow 之后,我仍在努力在我的@ControllerAdvice 中捕获此异常。 java.lang.IllegalStateException: io.undertow.server.RequestTooBigException: UT000020: Connection terminated as request was larger than 15728640你解决了吗? @PatrickBray,你找到解决方案了吗? 【参考方案1】:

也许它能够在某些特定情况下提供帮助。

我在处理undertow和异常处理方面遇到了同样的问题。

我的情况有点不同,我也有spring-security,所以像RequestTooBigException这样的异常是spring-security过滤器中的一些尝试从请求中读取信息时引起的。

作为一种解决方案,我使用了一个附加过滤器,该过滤器添加在导致该异常的第一个安全过滤器之前。 (对我来说是BearerTokenAuthenticationFilter.class

还有一个额外的过滤器可以处理这些异常:

 @Component
 @Slf4j
 public class SecurityRequestToBigExceptionHandlerFilter extends OncePerRequestFilter 

   @Override
   public void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
       FilterChain filterChain) throws ServletException, IOException 
     try 
       filterChain.doFilter(request, response);
      catch (IllegalStateException | RequestTooBigException | MultiPartParserDefinition.FileTooLargeException ex) 

       if (ex instanceof RequestTooBigException
           || ex instanceof MultiPartParserDefinition.FileTooLargeException) 
         writeResponse(response, ex);
         return;
       

       Throwable causeException = ex.getCause();
       if (nonNull(causeException)
           && (causeException instanceof RequestTooBigException
           || causeException instanceof MultiPartParserDefinition.FileTooLargeException)) 
         writeResponse(response, causeException);
         return;
       

       throw ex;
     
   

   private void writeResponse(HttpServletResponse response, Throwable ex) throws IOException 
     log.debug("Handle RequestToBig Exception with ", ex.getMessage());
     response.setStatus(HttpStatus.BAD_REQUEST.value());
     response.getWriter().write(ex.getMessage());
  
 

接下来我们需要改变的是安全配置。

这是我的简化安全配置:

 @EnableWebSecurity
 @RequiredArgsConstructor
 public class SecurityConfig extends WebSecurityConfigurerAdapter 

   @Autowired
   private final SecurityRequestToBigExceptionHandlerFilter requestToBigExceptionHandlerFilter;

   @Override
   public void configure(HttpSecurity http) throws Exception 
     http 
         .addFilterBefore(requestToBigExceptionHandlerFilter, BearerTokenAuthenticationFilter.class)
         .csrf().disable()
         .cors()
         .and()
         .authorizeRequests()
         .anyRequest().authenticated()
         .and()
         .oauth2ResourceServer().jwt();
   
 

【讨论】:

以上是关于Undertow 上传多部分文件超过设置值时抛出 RuntimeException的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 验证在更新相同的唯一值时抛出错误

S3 上传程序在尝试上传大文件时抛出异常

Firebase Storage 在图像上传时抛出错误(Nodejs)

使用 Undertow 的多部分表单数据示例

Multer在通过ng-file上传上传文件时抛出奇怪的错误

org.eclipse.jetty.io.EofException:上传大文件时抛出早期 EOF