使用SpringBoot 2.0进行文件上传验证

时间:2019-03-25 16:19:27

标签: spring-boot thymeleaf spring-validator embedded-tomcat-8

我正在使用SpringBoot 2.1.3.RELEASE + thymeleaf + tomcat 8.53嵌入式。我也有一个带有fileupload输入字段的表单。

如在文档中所写,我的DTO bean中具有MultiPartFile,并在application.properties中设置最大文件大小,如下所示:

spring.servlet.multipart.max-file-size=3MB
spring.servlet.multipart.max-request-size=3MB

它(一半)有效,因为如果我加载的文件大于3 mb,应用程序将崩溃,并且浏览器无法发送POST,并且控制器将永远无法到达。.

目前,我解决了一个糟糕的JQuery解决方案,但我想做的是与我拥有的所有其他文件类似的实现,例如:

JAVA BEAN

class Bean {
  @NotNull
  private String name;
}

胸腺

<form id="msform" action="" th:action="@{/save-fornitore}" th:object="${user}" method="post" enctype="multipart/form-data">

<input type="text" th:field="*{name}" th:text="${name}"></input>
<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></span>
<input type="submit"></input>
</form>

以及 messages.properties 文件中的一些自定义消息

NotNull.user.name=*NOME: \u0020 Il campo \u00E8 obbligatorio; \u0020\u0020

我已阅读到这是tomcat连接重置的问题;我找到了一个示例,但它仅适用于以前版本的SpringBoot:

@Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbedded() {

    TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();

    tomcat.addConnectorCustomizers((TomcatConnectorCustomizer) connector -> {
        if ((connector.getProtocolHandler() instanceof AbstractHttp11Protocol<?>)) {
            //-1 means unlimited
            ((AbstractHttp11Protocol<?>) connector.getProtocolHandler()).setMaxSwallowSize(-1);
        }
    });

    return tomcat;

}

我无法用SpringBoot 2.1.3做同样的事情。我想在处理其他字段时将错误绑定,如果文件太大,请返回表单页面。

我找到了添加该控制器返回错误页面的方法:

@ControllerAdvice
public class GlobalExceptionHandler {

  @ExceptionHandler(MultipartException.class)
  public String handleError1(MultipartException e, RedirectAttributes redirectAttributes) {

//  redirectAttributes.addFlashAttribute("message", e.getCause().getMessage());
    return "redirect:/errorPage";

  }

}

但是这种简单的重定向将流程重定向到一个空页面(至少我可以添加一条错误消息),从而丢失了论坛发布的所有数据(除了文件,我还有其他50个属性。)。

我什至发现可以在springboot 2.0上设置tomcat swallowSize属性,如下所示:

server.tomcat.max-swallow-size=-1

但是这些都没有帮助我做我想做的事。

P.S .:这个论坛发生了什么?几年前,它挽救了我的生命不止一次,但最近平均答复是每20个问题中就有1个。

------------------------------- 更新2 ------- ------------------------------

好吧,让我们重新启动一些代码示例和屏幕截图。首先,我正在使用JPA,SpringBoot 2.1.3,Thymeleaf。比起我简单的DTO调用 UserDTO

@Data //lombok
public class UserDTO {
  @Size(min=10, max=15)
  private String name;
  private MultipartFile file;
}

在我的资源/模板中,我有一个具有以下格式的welcome.html页面:

<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
  xmlns:th="http://www.thymeleaf.org">
<head><title>Form page</title></head>

<form id="myform" action="" th:action="@{/save-user}" th:object="${user}" method="post" enctype="multipart/form-data">

<input type="text" th:field="*{name}" />
<p th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></p>

<input type="file" th:field="*{file}" />
<p th:if="${#fields.hasErrors('file')}" th:errors="*{file}"></p>

<input type="submit" />
</form>

我的主控制器:

@GetMapping("/")
String index(Model model) {
    model.addAttribute("user", new UserDTO());
    return "welcome";
}

@PostMapping("/save-user")
String saveUser(@Valid @ModelAttribute("user") UserDTO user, BindingResult result, Model model) {
    if (result.hasErrors()) {
        System.out.println("ERROR");
        model.addAttribute("user", user);
        return "welcome";
    }
    return success; // ipotetic success page
}

我的application.properties设置为要上传3 MB的文件(默认值为1MB):

spring.servlet.multipart.max-file-size=3MB
spring.servlet.multipart.max-request-size=3MB

我还为每个Bean属性设置了一个自定义错误消息,例如 messages.properties 中的示例:

Size.user.name=Name Must be between 10 and 15

很明显,我的表单更大,但是问题只在multipartfile上,而这个例子基本上是完美的。示例表单如下所示:

如果我用较少(或更多)的字符数填充名称,并提交表单->操作'save-user'已丰富,则'user'ModelAttribute正在验证,并且如果有错误,控制器将返回同一页面,先前插入的数据已得到处理,并且错误显示如下:

enter image description here

相反,如果我尝试加载大于3MB的文件,则会导致连接崩溃。控制器无法到达,错误是:

 org.apache.tomcat.util.http.fileupload.FileUploadBase$ 
 SizeLimitExceededException: the request was rejected because its size (7529964) exceeds the configured maximum (3145728)
at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:808) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:256) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:280) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.connector.Request.parseParts(Request.java:2846) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.connector.Request.parseParameters(Request.java:3185) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.connector.Request.getParameter(Request.java:1116) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:84) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-9.0.16.jar:9.0.16]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.1.5.RELEASE.jar:5.1.5.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-9.0.16.jar:9.0.16]

在我的途中,我找到了2个解决方案,但两者都很粗糙:

1)如我的第一篇文章所述,配置tomcat吞咽大小:

spring.servlet.multipart.max-file-size=-3MB
spring.servlet.multipart.max-request-size=3MB

server.tomcat.max-swallow-size=-1 // this

并添加了控制器建议:

@ControllerAdvice
public class GlobalExceptionHandler {

   @ExceptionHandler(MultipartException.class)
   public String handleError1(MultipartException e, RedirectAttributes redirectAttributes) {

    return "redirect:/somePage";

  }

}

通过这种解决方法,我可以返回错误页面,但我会丢失用户插入的所有数据和错误消息(以复杂的形式,对于用户查看他的错误位置以及如何正确输入很有用)

2)使用自定义验证器疯狂配置雄猫:

spring.servlet.multipart.max-file-size=-1
spring.servlet.multipart.max-request-size=-1

server.tomcat.max-swallow-size=-1

并实现:

public class MultiPartFileValidator implements ConstraintValidator <FileValidator, MultipartFile> {

@Override
public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
    if ( ( (file.getSize() / 1024) / 1024) < 1) return true;
    else return false;
}

}

这样,如果我将 @FileValidator 批注放入DTO中的MultiPartFile中,则验证效果会很好,但是如果用户不小心上传了电影而不是其简历,我们将等待一些....

我想我没有比这更清楚了。

P.S .:这种经典图形真的很酷,清晰,简单。全新的产品胜于饥饿和疾病。图形更少..更多内容..

谢谢

0 个答案:

没有答案