How to handle MultipartException in Spring

两盒软妹~` 提交于 2019-12-21 16:50:32

问题


My configuration is here.

I made some modifications according to the answer in that post.

filterMultipartResolver

@Bean
public StandardServletMultipartResolver filterMultipartResolver() {
    return new StandardServletMultipartResolver();
}

AppInitializer

public class AppInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(final ServletContext servletContext) throws ServletException {
        // Create the 'root' Spring application context
        final WebApplicationContext context = getContext();
        // Manage the lifecycle of the root application context
        servletContext.addListener(new ContextLoaderListener(context));
        final Dynamic dispatcher = servletContext.addServlet("DispatcherServlet", new DispatcherServlet(context));
        dispatcher.setMultipartConfig(getMultipartConfigElement());
        dispatcher.setLoadOnStartup(1);
        dispatcher.addMapping("/");
    }

    private static AnnotationConfigWebApplicationContext getContext() {
        final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
        context.register(AppConfig.class);
        return context;
    }

    private static MultipartConfigElement getMultipartConfigElement(){
        return new MultipartConfigElement(Props.FILE_TMP_DIRECTORY, 3 * 1024 * 1024, 3 * 1024 * 1024, 3 * 1024 * 1024);
    }
}

I followed the suggestions from this post, and included this MultipartExceptionHandler

public class MultipartExceptionHandler extends OncePerRequestFilter {
    static final Logger log = LoggerFactory.getLogger(MultipartExceptionHandler.class);

    @Override
    protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException {
        try {
            filterChain.doFilter(request, response);
        } catch (final MultipartException me) {
            log.error(me.getMessage());
            response.sendRedirect(UrlUtils.buildFullRequestUrl(request) + "&error=size-limit");
        }
    }
}

registering it in

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

    @Override
    protected void beforeSpringSecurityFilterChain(final ServletContext servletContext) {
        insertFilters(servletContext, new MultipartExceptionHandler());
        insertFilters(servletContext, new MultipartFilter());
    }
}

When I try to upload a file exceeding the maximum size this is what I get in the logs

DEBUG 2015-03-17 19:54:18,372: org.springframework.web.multipart.support.MultipartFilter - Using MultipartResolver 'filterMultipartResolver' for MultipartFilter
DEBUG 2015-03-17 19:54:18,372: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'filterMultipartResolver'
DEBUG 2015-03-17 19:54:18,372: org.springframework.web.multipart.support.MultipartFilter - Resolving multipart request [/registrazione] with MultipartFilter
ERROR 2015-03-17 19:54:18,384: it.openex.pmfew.filters.MultipartExceptionHandler - 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 (4522604) exceeds the configured maximum (3145728)
DEBUG 2015-03-17 19:54:18,386: org.springframework.web.multipart.support.MultipartFilter - Using MultipartResolver 'filterMultipartResolver' for MultipartFilter
DEBUG 2015-03-17 19:54:18,386: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'filterMultipartResolver'
DEBUG 2015-03-17 19:54:18,386: org.springframework.web.multipart.support.MultipartFilter - Resolving multipart request [/registrazione] with MultipartFilter
ERROR 2015-03-17 19:54:18,386: it.openex.pmfew.filters.MultipartExceptionHandler - 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 (4522604) exceeds the configured maximum (3145728)

It repeats itself 7 times, and the redirect specified in the filter doesn't happen: I simply get a blank page.

I'm not sure what happens because sometime adding a debug breakpoint in the filter makes it work, or even adding a Thread.sleep(100)

Update 1

Some more information on the issue.

Every time I restart Tomcat the filter could be called 2 times or 7 times. There is only one POST call.

The response in chrome is (failed) net::ERR_CONNECTION_RESET.

The request is an instance of org.apache.catalina.connector.RequestFacade, containing an instance of org.apache.catalina.connector.Request. Inside the latter the attribute partsParseException is carrying the exception showing in the log. The request is identical every time.

There are 4 filters in the chain:

  • MultipartExceptionHandler (the filter I added)
  • org.springframework.web.multipart.support.MultipartFilter
  • org.springframework.web.filter.DelegatingFilterProxy
  • org.apache.tomcat.websocket.server.WsFilter

I upgraded Tomcat to the last version: 8.0.20.

I tried to make a redirect to a simple url like /, and catching all kind of exceptions inside the filter, included the ones potentially thrown by the catch block.

The result is this

public class MultipartExceptionHandler extends OncePerRequestFilter {
    static final Logger log = LoggerFactory.getLogger(MultipartExceptionHandler.class);
    @Override
    protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain) throws ServletException, IOException {
        try {
            filterChain.doFilter(request, response);
        } catch (final MultipartException me) {
            try{
                log.error(me.getMessage());
                response.sendRedirect(UrlMap.HOME);
            } catch (final Exception e){
                log.error(e.getMessage());
            }

        }catch (final Exception e){
            log.error(e.getMessage());
        }
    }
}

What is disturbing is that it doesn't seem to behave deterministically: using Intellij Idea, sometime if I put a breakpoint inside the catch block of `MultipartExceptionHandler' and then resume the program it works flawlessly.

If it doesn't, I just have to restart Tomcat 1 or 2 times until the trick works again.

Without the breakpoint the program never works.

Update 2

I tested a little more the behaviour of the application.

I discovered that simply it works out of the box every now and then, without restarting Tomcat or making anything fancy.

It's a matter of retrying to upload the file until it goes well, pushing the browser back button to return to the upload page in case of error.

It works 1 out of 4-5 times, without any apparent repeating pattern.

This is the stacktrace if it fails. In the logs it is repeated 2 times consecutively and there is nothing else after it.

INFO  2015-03-18 12:21:59,870: it.openex.pmfew.filters.MultipartExceptionHandler - #############################
INFO  2015-03-18 12:21:59,870: it.openex.pmfew.filters.MultipartExceptionHandler - /registrazione?execution=e3s2
INFO  2015-03-18 12:21:59,870: it.openex.pmfew.filters.MultipartExceptionHandler - #############################
DEBUG 2015-03-18 12:21:59,870: org.springframework.web.multipart.support.MultipartFilter - Using MultipartResolver 'filterMultipartResolver' for MultipartFilter
DEBUG 2015-03-18 12:21:59,870: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'filterMultipartResolver'
DEBUG 2015-03-18 12:21:59,870: org.springframework.web.multipart.support.MultipartFilter - Resolving multipart request [/registrazione] with MultipartFilter
ERROR 2015-03-18 12:21:59,870: it.openex.pmfew.filters.MultipartExceptionHandler - 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 (4522604) exceeds the configured maximum (3145728)
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 (4522604) exceeds the configured maximum (3145728)
    at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:99)
    at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:77)
    at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:76)
    at org.springframework.web.multipart.support.MultipartFilter.doFilterInternal(MultipartFilter.java:108)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at it.openex.pmfew.filters.MultipartExceptionHandler.doFilterInternal(MultipartExceptionHandler.java:29)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:610)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:516)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1086)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:659)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:223)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1558)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1515)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (4522604) exceeds the configured maximum (3145728)
    at org.apache.catalina.connector.Request.parseParts(Request.java:2792)
    at org.apache.catalina.connector.Request.getParts(Request.java:2636)
    at org.apache.catalina.connector.RequestFacade.getParts(RequestFacade.java:1083)
    at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:84)
    ... 27 more
Caused by: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (4522604) exceeds the configured maximum (3145728)
    at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:811)
    at org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:256)
    at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:280)
    at org.apache.catalina.connector.Request.parseParts(Request.java:2725)
    ... 30 more

Instead, if it goes well, the stacktrace is showed only once and than this is appended (I removed some parts because of the size limit of the post)

INFO  2015-03-18 12:20:38,789: it.openex.pmfew.filters.MultipartExceptionHandler - #############################
INFO  2015-03-18 12:20:38,789: it.openex.pmfew.filters.MultipartExceptionHandler - Url called -> /registrazione?execution=e1s2&error=size-limit
INFO  2015-03-18 12:20:38,789: it.openex.pmfew.filters.MultipartExceptionHandler - #############################
DEBUG 2015-03-18 12:20:38,789: org.springframework.web.multipart.support.MultipartFilter - Using MultipartResolver 'filterMultipartResolver' for MultipartFilter
DEBUG 2015-03-18 12:20:38,789: org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'filterMultipartResolver'
DEBUG 2015-03-18 12:20:38,789: org.springframework.web.multipart.support.MultipartFilter - Request [/registrazione] is not a multipart request
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 1 of 13 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 2 of 13 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.context.HttpSessionSecurityContextRepository - No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@2fabad6. A new one will be created.
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 3 of 13 in additional filter chain; firing Filter: 'HeaderWriterFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 4 of 13 in additional filter chain; firing Filter: 'CharacterEncodingFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 5 of 13 in additional filter chain; firing Filter: 'CsrfFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 6 of 13 in additional filter chain; firing Filter: 'LogoutFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 7 of 13 in additional filter chain; firing Filter: 'UsernamePasswordAuthenticationFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 8 of 13 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 9 of 13 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 10 of 13 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 11 of 13 in additional filter chain; firing Filter: 'SessionManagementFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 12 of 13 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit at position 13 of 13 in additional filter chain; firing Filter: 'FilterSecurityInterceptor'
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.access.intercept.FilterSecurityInterceptor - Secure object: FilterInvocation: URL: /registrazione?execution=e1s2&error=size-limit; Attributes: [permitAll]
DEBUG 2015-03-18 12:20:38,789: org.springframework.security.web.FilterChainProxy - /registrazione?execution=e1s2&error=size-limit reached end of additional filter chain; proceeding with original chain
DEBUG 2015-03-18 12:20:38,789: org.springframework.web.servlet.DispatcherServlet - DispatcherServlet with name 'DispatcherServlet' processing GET request for [/registrazione]
DEBUG 2015-03-18 12:20:38,789: org.springframework.webflow.mvc.servlet.FlowHandlerMapping - Mapping request with URI '/registrazione' to flow with id 'registrazione'
DEBUG 2015-03-18 12:20:38,789: org.springframework.webflow.executor.FlowExecutorImpl - Resuming flow execution with key 'e1s2
DEBUG 2015-03-18 12:20:38,790: org.springframework.webflow.conversation.impl.SessionBindingConversationManager - Locking conversation 1
DEBUG 2015-03-18 12:20:38,790: org.springframework.webflow.execution.repository.impl.DefaultFlowExecutionRepository - Getting flow execution with key 'e1s2'
DEBUG 2015-03-18 12:20:38,790: org.springframework.webflow.definition.registry.FlowDefinitionRegistryImpl - Getting FlowDefinition with id 'registrazione'
DEBUG 2015-03-18 12:20:38,809: org.springframework.webflow.execution.repository.impl.DefaultFlowExecutionRepository - Putting flow execution '[FlowExecutionImpl@23da7555 flow = 'registrazione', flowSessions = list[[FlowSessionImpl@4c63c9fd flow = 'registrazione', state = 'companyLogo', scope = map['viewScope' -> map['fileForm' -> it.openex.pmcommonw.form.FileForm@ae6eeae], 'menuDTO' -> list[it.openex.pmfew.dtos.MenuEntryDTO@2fcdd386, it.openex.pmfew.dtos.MenuEntryDTO@3ba665a6, it.openex.pmfew.dtos.MenuEntryDTO@5767063d], 'userCompanyInfoForm' -> it.openex.pmcommonw.form.UserCompanyInfoForm@6ac911e1]]]]' into repository
DEBUG 2015-03-18 12:20:38,810: org.springframework.webflow.execution.repository.impl.DefaultFlowExecutionRepository - Adding snapshot to group with id 2
DEBUG 2015-03-18 12:20:38,813: org.springframework.webflow.conversation.impl.SessionBindingConversationManager - Unlocking conversation 1
DEBUG 2015-03-18 12:20:38,813: org.springframework.web.servlet.DispatcherServlet - Successfully completed request
DEBUG 2015-03-18 12:20:38,813: org.springframework.security.web.access.ExceptionTranslationFilter - Chain processed normally

Update 3

Until now I tried to upload a file 4.5MB large.

Using a 30MB file instead it never works.

Update 4

Looking at the POST request in Chrome's Developer Tools, on the Timing tab, I can see the request was stalled. More info about this state.

Firefox works every time with the 4.5MB large file, redirecting correctly to the error page. With a larger file (say 7MB) it doesn't work and the browser returns the message

The connection was reset

The connection to the server was reset while the page was loading.

Update 5

Switching inside MultipartExceptionHandler the row

response.sendRedirect(UrlUtils.buildFullRequestUrl(request) + "&error=size-limit");

with

final RequestDispatcher requestDispatcher = request.getRequestDispatcher("/");
requestDispatcher.forward(request, response);

it works 3 out of 4 times in Chrome, and with large files it fails always as usual. Nothing changes in the logs.


回答1:


It sounds like multiple requests. According to the log the MultipartExceptionHandler is called when a Exception occurs.

Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException:

Maybe you can add a logger after protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain), set an break point here and check the request. It's possible that the request is null or something else because of

Could not parse multipart servlet request; ... the request was rejected because its size ...

and in this case UrlUtils.buildFullRequestUrl(request)will also throw a exception which is not catched.

public final class UrlUtils {
    public static String buildFullRequestUrl(HttpServletRequest r) {
         return buildFullRequestUrl(r.getScheme(), r.getServerName(), r.getServerPort(), r.getRequestURI(),
                 r.getQueryString());
    }
...

Try to set the redirect uri manually like response.sendRedirect(uri + "&error=size-limit"). This implies that the response must not be null.

Try to write a test for your file upload with mock requests and mock exception and assert the expected redirect uri.



来源:https://stackoverflow.com/questions/29107666/how-to-handle-multipartexception-in-spring

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!