Как обрабатывать MultipartException в Spring

Моя конфигурация здесь.

Я внес некоторые изменения в соответствии с ответом в этом сообщении.

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);
    }
}

Я следовал предложениям из этот пост, и включил это 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");
        }
    }
}

зарегистрировав его в

public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

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

Когда я пытаюсь загрузить файл, превышающий максимальный размер, это то, что я получаю в журналах

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)

Повторяется 7 раз, а указанного в фильтре перенаправления не происходит: просто получаю пустую страницу.

Я не уверен, что происходит, потому что иногда добавление точки останова отладки в фильтр заставляет его работать или даже добавление Thread.sleep(100)

Обновление 1

Еще немного информации по вопросу.

Каждый раз, когда я перезапускаю Tomcat, фильтр может вызываться 2 или 7 раз. Существует только один вызов POST.

Ответ в хроме (failed) net::ERR_CONNECTION_RESET.

Запрос представляет собой экземпляр org.apache.catalina.connector.RequestFacade, содержащий экземпляр org.apache.catalina.connector.Request. Внутри последнего атрибут partsParseException содержит исключение, отображаемое в журнале. Запрос каждый раз одинаков.

В цепочке 4 фильтра:

  • MultipartExceptionHandler (фильтр, который я добавил)
  • org.springframework.web.multipart.support.MultipartFilter
  • org.springframework.web.filter.DelegatingFilterProxy
  • org.apache.tomcat.websocket.server.WsFilter

Я обновил Tomcat до последней версии: 8.0.20.

Я попытался сделать перенаправление на простой URL-адрес, такой как /, и перехватил все виды исключений внутри фильтра, включая те, которые потенциально могут быть вызваны блоком catch.

Результат такой

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());
        }
    }
}

Что беспокоит, так это то, что он, похоже, не ведет себя детерминировано: используя Intellij Idea, иногда, если я ставлю точку останова внутри блока catch MultipartExceptionHandler, а затем возобновляю программу, она работает безупречно.

Если это не так, мне просто нужно перезапустить Tomcat 1 или 2 раза, пока трюк снова не сработает.

Без точки останова программа никогда не работает.

Обновление 2

Я еще немного протестировал поведение приложения.

Я обнаружил, что время от времени он просто работает «из коробки», без перезапуска Tomcat или каких-либо причудливых действий.

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

Он работает 1 из 4-5 раз, без видимого повторяющегося шаблона.

Это трассировка стека в случае сбоя. В логах повторяется 2 раза подряд и больше ничего после него нет.

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

Вместо этого, если все идет хорошо, трассировка стека отображается только один раз, а затем добавляется (я удалил некоторые части из-за ограничения размера сообщения)

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

Обновление 3

До сих пор я пытался загрузить файл размером 4,5 МБ.

Использование файла размером 30 МБ вместо этого никогда не работает.

Обновление 4

Глядя на запрос POST в инструментах разработчика Chrome на вкладке «Время», я вижу, что запрос был остановлен. Подробнее об этом состоянии.

Firefox каждый раз работает с большим файлом размером 4,5 МБ, корректно перенаправляя на страницу с ошибкой. С большим файлом (скажем, 7 МБ) это не работает, и браузер возвращает сообщение

The connection was reset

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

Обновление 5

Переключение внутри MultipartExceptionHandler строки

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

с участием

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

он работает 3 из 4 раз в Chrome, а с большими файлами всегда выходит из строя, как обычно. В логах ничего не меняется.


person Dario Zamuner    schedule 17.03.2015    source источник
comment
Я знаю, что опаздываю на вечеринку, но могло ли это быть из-за того, что сервер Tomcat отменял запрос, если он был слишком большим? В Spring Boot 2 вы можете установить следующее свойство: server.tomcat.max-swallow-size=100MB Подробнее здесь: stackoverflow.com/a /49098174/519035   -  person AbstractVoid    schedule 28.01.2019


Ответы (2)


Похоже на несколько запросов. Согласно журналу, MultipartExceptionHandler вызывается при возникновении исключения.

Не удалось проанализировать составной запрос сервлета; вложенным исключением является java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException:

Может быть, вы можете добавить логгер после protected void doFilterInternal(final HttpServletRequest request, final HttpServletResponse response, final FilterChain filterChain), установить здесь точку останова и проверить запрос. Возможно, что запрос нулевой или что-то еще из-за

Не удалось проанализировать составной запрос сервлета; ...запрос был отклонен из-за его размера...

и в этом случае UrlUtils.buildFullRequestUrl(request) также выдаст исключение, которое не будет перехвачено.

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

Попробуйте установить URI перенаправления вручную, например response.sendRedirect(uri + "&error=size-limit"). Это означает, что ответ не должен быть нулевым.

Попробуйте написать тест для загрузки файла с фиктивными запросами и фиктивным исключением и подтвердите ожидаемый uri перенаправления.

person Thomas Haarbach    schedule 17.03.2015
comment
Спасибо, Томас, я обновил вопрос в соответствии с вашими предложениями. - person Dario Zamuner; 18.03.2015
comment
Можете ли вы добавить полную трассировку стека из ваших журналов? См. также журнал Catalina. - person Thomas Haarbach; 18.03.2015

Я в той же ситуации: мой MultipartExceptionHandler вызывается несколько раз (3), даже если запрос равен 1 (я вижу это в своем инспекторе браузера).

Это приводит меня к ошибке: ERR_CONNECTION_RESET

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

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            filterChain.doFilter(request, response);
        } catch (MaxUploadSizeExceededException e) {
            handle(request, response, e);
        } catch (ServletException e) {
            if(e.getRootCause() instanceof MaxUploadSizeExceededException) {
                handle(request, response, (MaxUploadSizeExceededException) e.getRootCause());
            } else {
                throw e;
            }
        } finally {
            String url = Utils.buildFullRequestUrl(request);
            log.debug("URL: " + url);
        }
    }

    private void handle(HttpServletRequest request, HttpServletResponse response, MaxUploadSizeExceededException e) throws IOException {
        String redirect = UrlUtils.buildFullRequestUrl(request) + "?upload-error=true";
        response.sendRedirect(redirect);
    }


}

У вас есть какие-нибудь предложения?

Спасибо.

РЕШЕНО

В моем случае проблема заключалась в том, что я пропустил установку параметра Tomcat maxSwallowSize.

person HK15    schedule 18.03.2021