When I upload a file of size that exceeds the configured max file size, the response returned is not very pretty or useful to my JS UI. So, I want to catch it and handle it. But, the issue is error is thrown before my controller is entered. Thus, I'm at a loss as to where to place my error handling code. One idea I'm toying with is defining a filter and catching it there. Is that the normal place to do it? The stack trace I'm seeing is:
at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl$FileItemStreamImpl$1.raiseError(FileUploadBase.java:628) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.http.fileupload.util.LimitedInputStream.checkLimit(LimitedInputStream.java:76) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.http.fileupload.util.LimitedInputStream.read(LimitedInputStream.java:135) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at java.io.FilterInputStream.read(FilterInputStream.java:107) ~[na:1.8.0_121]
at org.apache.tomcat.util.http.fileupload.util.Streams.copy(Streams.java:98) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.http.fileupload.util.Streams.copy(Streams.java:68) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:293) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.connector.Request.parseParts(Request.java:2902) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.connector.Request.parseParameters(Request.java:3242) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.connector.Request.getParameter(Request.java:1136) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:84) ~[spring-web-5.0.9.RELEASE.jar!/:5.0.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.9.RELEASE.jar!/:5.0.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.0.9.RELEASE.jar!/:5.0.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.9.RELEASE.jar!/:5.0.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_121]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_121]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]
My controller action looks like this:
@PostMapping("/upload")
@ResponseBody
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
String fileName = storageService.store(file);
String fileUrl = "/api/file/" + fileName;
return "{\"fileUrl\":\"" + fileUrl + "\"}";
}
Though that's not very important because that code is not entered because the exception is thrown int he thread first.
As expected, adding an exception handler to the controller did not catch the exception for the same reason.
@ExceptionHandler(FileUploadBase.FileSizeLimitExceededException.class)
public String handlefileSizeLimitExceeded(FileUploadBase.FileSizeLimitExceededException exc) {
return "{\"error\":\"file too big\"}";
}
Note: I am not asking how to change the file size max. I already know how to do that. My objective is to report when the user attempts to upload a file of size greater than the maximum.
You need to catch MaxUploadSizeExceededException.class
@ExceptionHandler(MaxUploadSizeExceededException.class)
public String handleFileSizeLimitExceeded(MaxUploadSizeExceededException exc) {
return "{\"error\":\"file too big\"}";
}
I had the same problem, the other answer did not work for me so kept looking and found this: https://www.baeldung.com/spring-maxuploadsizeexceeded
(as always, excellent guides at baeldung.com, thanks!).
Check the Tomcat configuration part, it explains why we keep running into the problem even when using an ExceptionHandler
I will share what worked for me, using Spring Boot v2.3.4 with the embedded Tomcat. I wanted to allow files of 10 MB and show an error message to the user
Added this to application.yml
#Configuration for size of files that can be uploaded
#Set Tomcat to accept any file size for failed upload
server.tomcat.max-swallow-size: -1
#set the Spring max file size for upload
spring.servlet.multipart:
max-file-size: 10MB
max-request-size: 10MB
Then I added a @ControllerAdvice
like the guide said. Note that just adding an @ExceptionHandler
to the controller where the upload is handled did not work.
@Slf4j
@ControllerAdvice
public class FileUploadExceptionAdvice {
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<String> handleMaxUploadSizeExceededException(Exception ex){
log.warn("Handling MaxUploadSizeExceededException: {}",ex.getMessage());
return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE)
.contentType(MediaType.TEXT_PLAIN)
.body("Message for user");
}
}
In my case I wanted to return some plain text to be used in the JS library that handles the upload and shows the error (this one is excellent:https://www.dropzonejs.com/).
You can change your handling depending on what you need : some JSON error message, or show a page with an error message like the guide.
Note that you can retrieve the Locale
in the exception handler if you need i18n for your error message, just add it as a parameter of the method. Looks something like this:
@Slf4j
@ControllerAdvice
public class FileUploadExceptionAdvice {
@Value("${spring.servlet.multipart.max-file-size}")
private String maxFileSize;
private final MessageSource messageSource;
public FileUploadExceptionAdvice(MessageSource messageSource){
this.messageSource=messageSource;
}
@ExceptionHandler(MaxUploadSizeExceededException.class)
public ResponseEntity<String> handleMaxUploadSizeExceededException(Locale locale, Exception ex){
String errorMessage = messageSource.getMessage("max.upload.size.exceeded", new Object[]{maxFileSize}, locale);
//use errorMessage in return handling
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With