Я использую 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 проверяется, и при наличии ошибок контроллер возвращает ту же страницу, предыдущие введенные данные были сохранены, и ошибка отображается следующим образом:
Вместо этого, если я пытаюсь загрузить файл размером более 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 .: Эта классическая графика действительно классная, понятная, простая. Совершенно новый хуже, чем голод и болезни. Меньше графики .. больше содержания ..
Спасибо всем