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