Проверка загрузки файла с помощью SpringBoot 2.0 - PullRequest
0 голосов
/ 25 марта 2019

Я использую SpringBoot 2.1.3.RELEASE + thymeleaf + tomcat 8.53 встроенный. У меня также есть форма с полем ввода fileupload.

Как написано в документации, у меня в компоненте DTO есть MultiPartFile, и я устанавливаю максимальный размер файла в application.properties следующим образом:

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

И это (наполовину) работает, потому что, если я загружаю файл размером более 3 МБ, происходит сбой приложения, и браузер не может отправить POST, и контроллер никогда не достигает ..

Пока что я решил с помощью ужасной работы с JQuery, но я хотел бы сделать реализацию, аналогичную всем остальным полям, которые у меня есть, например:

JAVA BEAN

class Bean {
  @NotNull
  private String name;
}

Thymeleaf

<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 file

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 атрибутов, кроме файла ..).

Я даже обнаружил, что свойства tomcat swallowSize могут быть установлены в springboot 2.0 следующим образом:

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

Но ничто из этого не помогает мне делать то, что я хочу.

П.С .: что случилось с этим форумом? Несколько лет назад это спасло мне жизнь не раз, но в последнее время средний ответ составляет примерно 1 раз в 20 вопросов.

------------------------------- ОБНОВЛЕНИЕ 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 МБ (по умолчанию 1 МБ):

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

Я также установил пользовательское сообщение об ошибке для каждого атрибута Бина, например, в messages.properties У меня есть:

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

Ясно, что моя форма довольно большая, но проблема только в мультифайле, чем этот пример, но принципиально, но идеально. Пример формы выглядит так:

Если я заполню имя меньшим (или большим) числом символов и отправлю форму -> действие 'save-user' было обогащено, 'user' ModelAttribute проверяется, и при наличии ошибок контроллер возвращает ту же страницу, предыдущие введенные данные были сохранены, и ошибка отображается следующим образом:

enter image description here

Вместо этого, если я пытаюсь загрузить файл размером более 3 МБ, происходит сбой соединения. контроллер никогда не достигает и ошибка:

 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) Сумасшедшая настройка tomcat с пользовательским валидатором:

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 в мой MultiPartFile внутри моего DTO, проверка работает отлично, но если пользователь случайно загрузит фильм вместо своего резюме, мы немного подождем ...

Полагаю, я не могу быть более ясным, чем это ..

P.S .: Эта классическая графика действительно классная, понятная, простая. Совершенно новый хуже, чем голод и болезни. Меньше графики .. больше содержания ..

Спасибо всем

...