I would like to be able to upload images to the server, handle errors and exceptions gracefully, with error messages displayed to the user in the form, and, ideally, only using the existing barebones Spring Boot and Thymeleaf.
Using the gs-uploading-files sample project I can upload files to the server using Spring Boot and Thymeleaf. In application.properties, I set spring.http.multipart.max-file-size=1MB and spring.http.multipart.max-request-size=1MB . However, some security and validation problems are not resolved when downloading files larger than 1 MB.
Any file can be uploaded. For example, an html file can be uploaded and thus hosted on a server. How can files be limited in type? Can they be verified on the page before sending the request? If I have several ways to upload images, how can I check all MultipartFiles?
Users may try to overlap large files that exceed the default limits of Spring and embedded Tomcat. This results in org.springframework.web.multipart.MultipartException not being handled by Spring. How can the file size be checked before trying to download? In case this bypasses, can any exceptions to download files detected by Spring give a nice error message?
The Spring error page is not used by default as a backup for all exceptions. The MultipartException function returns a Tomcat full-glass exception page (see Log 1).
I was looking to try and find and implement a set of solutions.
The step to fixing number 1 is to change the handleFileUpload check the type of content, reject files that don't work !file.getContentType().toLowerCase().startsWith("image") . Will it always work? Can an attacker get around this? And how can I check each MultipartFile to keep the need to remember this every time?
@PostMapping("/") public String handleFileUpload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes) throws MultipartException, IllegalStateException { if (file != null && file.getContentType() != null && !file.getContentType().toLowerCase().startsWith("image")) throw new MultipartException("not img"); storageService.store(file); redirectAttributes.addFlashAttribute("message", "You successfully uploaded " + file.getOriginalFilename() + "!"); return "redirect:/"; }
Adding @ExceptionHandler does not work, it just does not get called.
@ExceptionHandler({ SizeLimitExceededException.class, MultipartException.class, java.lang.IllegalStateException.class }) public ModelAndView handleError(HttpServletRequest req, Exception e) { // error("Request: " + req.getRequestURL() + " raised " + ex); ModelAndView mav = new ModelAndView(); mav.addObject("exception", e); mav.addObject("url", req.getRequestURL()); mav.addObject("timestamp", new Date()); mav.addObject("error", e.getClass()); mav.addObject("message", e.getMessage()); mav.addObject("status", HttpStatus.INTERNAL_SERVER_ERROR); mav.setViewName("error"); return mav; }
The number 3 can be resolved by the global exception handler for all exceptions. (explained in detail in this post ). However, I am concerned that it might cancel the controller-level handler.
package hello; import java.util.Date; import javax.servlet.http.HttpServletRequest; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.servlet.ModelAndView; @ControllerAdvice class GlobalDefaultExceptionHandler { public static final String DEFAULT_ERROR_VIEW = "error"; @ExceptionHandler(value = Exception.class) public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
I tried this answer , which handles the exception, but returns an error page. I would like to go back to the original page and display a good error message.
Magazine 1:
HTTP Status 500 - Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576) type Exception report message Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576) description The server encountered an internal error that prevented it from fulfilling this request. exception org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982) org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872) javax.servlet.http.HttpServlet.service(HttpServlet.java:648) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) javax.servlet.http.HttpServlet.service(HttpServlet.java:729) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) root cause org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576) org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:111) org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:85) org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:76) org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1099) org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:932) org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872) javax.servlet.http.HttpServlet.service(HttpServlet.java:648) org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) javax.servlet.http.HttpServlet.service(HttpServlet.java:729) org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) root cause java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576) org.apache.catalina.connector.Request.parseParts(Request.java:2871) org.apache.catalina.connector.Request.parseParameters(Request.java:3176) org.apache.catalina.connector.Request.getParameter(Request.java:1110) org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381) org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:70) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) root cause org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576) org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:811) org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:256) org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:280) org.apache.catalina.connector.Request.parseParts(Request.java:2801) org.apache.catalina.connector.Request.parseParameters(Request.java:3176) org.apache.catalina.connector.Request.getParameter(Request.java:1110) org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381) org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:70) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) note The full stack trace of the root cause is available in the Apache Tomcat/8.5.5 logs. Apache Tomcat/8.5.5