首页 > 解决方案 > Tomcat:如何使用 Spring Boot 和 Thymeleaf 处理 SizeLimitExceededException

问题描述

我正在使用 Spring Boot 和 Thymeleaf 版本:2.4.10

我有一个非常基本的小项目设置,其中包含用户可以上传文件的联系表格。我正在尝试验证控制器中的用户输入,如下所示:

@RequestMapping(value = "/sendHTMLMail", method = POST)
    public String sendHTMLMail(@Valid final ContactEmail contactEmail, BindingResult bindingResult, final Locale locale) throws MessagingException, IOException {

        if (bindingResult.hasErrors()) {
            return "contactForm.html";
        }
        this.emailService.sendHTMLMail(contactEmail);
        this.emailService.sendHTMLMailToClient(contactEmail);
        return "contactForm.html";

    }

我的 HTML 模板看起来像这样

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Very Simple Contact Form</title>

    <form action="#" th:action="@{/sendHTMLMail}" th:object="${contactEmail}" enctype="multipart/form-data"
          method="post">

        <label for="clientFirstName">First name:</label><br>
        <input type="text" id="clientFirstName" th:field="*{clientFirstName}"><br>
        <label th:if="${#fields.hasErrors('clientFirstName')}" th:errors="*{clientFirstName}"></label><br>

        <label for="clientLastName">Last name:</label><br>
        <input type="text" id="clientLastName" th:field="*{clientLastName}"><br>
        <label th:if="${#fields.hasErrors('clientLastName')}" th:errors="*{clientLastName}"></label><br>

        <label for="clientEmail">Email address:</label><br>
        <input type="text" id="clientEmail" th:field="*{clientEmail}"><br>
        <label th:if="${#fields.hasErrors('clientEmail')}" th:errors="*{clientEmail}"></label><br>

        <label for="emailText">Message:</label><br>
        <input type="text" id="emailText" th:field="*{emailText}"><br>
        <label th:if="${#fields.hasErrors('emailText')}" th:errors="*{emailText}"></label><br>

        <label for="attachments">Attachments:</label><br>
        <input type="file" id="attachments" th:field="*{attachments}" multiple/><br>
        <label th:if="${#fields.hasErrors('attachments')}" th:errors="*{attachments}"></label><br>
        <input type="submit" name="send" value="Send">

    </form>
</head>
<body>

</body>
</html>

我已将我的 tomcat 的属性设置为最大上传大小,如下所示

########################Tomcat file size config############################
spring.servlet.multipart.max-file-size=50MB
spring.servlet.multipart.max-request-size=50MB
server.tomcat.max-swallow-size=50MB

我要验证的模型包含以下属性:

 @NotNull
    @Size(min = 2, max = 100)
    private String clientFirstName;
    @NotNull
    @Size(min = 2, max = 100)
    private String clientLastName;
    @NotNull
    @Pattern(regexp=".+@.+\\..+", message="Your Email must contain: @[domain].com/net/org etc.")
    private String clientEmail;
    @NotNull
    @Size(min = 10, max = 500)
    private String emailText;
    @ContactFileConstraint
    private MultipartFile[] attachments;

我的自定义验证器如下所示:

public class ClientFileValidator implements
        ConstraintValidator<ContactFileConstraint, MultipartFile[]> {
    private static final long VALID_FILE_SIZE = 5242880;

    @Override
    public void initialize(ContactFileConstraint constraintAnnotation) {
        ConstraintValidator.super.initialize(constraintAnnotation);
    }

    @Override
    public boolean isValid(MultipartFile[] multipartFiles, ConstraintValidatorContext constraintValidatorContext) {
        return isValidLength(multipartFiles) && isValidSize(multipartFiles) && isValidType(multipartFiles);

    }

    private boolean isValidLength(MultipartFile[] multipartFiles) {
       return multipartFiles.length <= 5;
    }

    private boolean isValidSize(MultipartFile[] multipartFiles) {
        for (int file = 0; file < multipartFiles.length; file++) {
            if (multipartFiles[file].getSize() > VALID_FILE_SIZE) {
                return false;
            }
        }
        return true;
    }

    private boolean isValidType(MultipartFile[] multipartFiles) {
        for (int file = 0; file < multipartFiles.length; file++) {
            if (!"image/png".equals(multipartFiles[file].getContentType()) &&
                    !"image/jpeg".equals(multipartFiles[file].getContentType())) {
                return false;
            }
        }
        return true;
    }

}

当我尝试上传大于自定义验证器中指定的文件且小于指定的最大上传大小的文件时,一切正常,我可以在重新加载的页面上看到错误,但是当大小超过 50MB 时,Tomcat 本身会拒绝带有SizeLimitExceededException的请求它登陆默认浏览器页面连接中断。这可以用 javascript 解决,所以它对用户更友好,但我想特别问一下是否有办法在 Spring Boot 中处理 tomcat 异常并通过来自服务器端的重定向响应来处理这个问题。我已经查看了其他解决方案关于增加server.tomcat.max-swallow-size的问题,但您可以想象有人总是可以尝试上传更大的文件。

我尝试了以下选项:

  1. 使用 @ControllerAdvice 注释的自定义类:

@ControllerAdvice

public class FileUploadExceptionAdvisor {
    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public String maxUploadSizeExceptionHandler(MaxUploadSizeExceededException e, RedirectAttributes redirectAttributes) {

        redirectAttributes.addFlashAttribute("attachments", e.getCause().getMessage());
        redirectAttributes.addAttribute("contactEmail", new ContactEmail());
        return "contactForm";

    }
    @ExceptionHandler(SizeLimitExceededException.class)
    public ResponseEntity<?> handleMaxUploadSizeExceededException(SizeLimitExceededException e){
        return ResponseEntity.notFound().build();
    }
}
  1. 在我的控制器中实现HandlerExceptionResolver

@覆盖

    public ModelAndView resolveException(
            HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse,
            Object o,
            Exception e) {

        ModelAndView modelAndView = new ModelAndView("BasicHTMLFile");

        if (e instanceof SizeLimitExceededException) {
            logger.error(e);
        }
        if(e instanceof MaxUploadSizeExceededException){
            logger.error(e);
        }

        return modelAndView;
    }

标签: javaspring-boottomcatthymeleaf

解决方案


推荐阅读