2013-05-28 50 views
0

我的Spring MVC應用程序的功能之一是上傳大文件(大約500MB),這些文件應該稍後處理。上傳大文件後,Spring MVC會話屬性丟失

控制器是:

@RequestMapping("/folderManagement/") 
@Controller("FolderFormController") 
@SessionAttributes({ "tabTitle", "selectedFolder" }) 
public class FolderFormController { 
    @RequestMapping(method = RequestMethod.GET, value = FOLDER_FORM) 
    protected ModelAndView showFolderManager(@RequestParam(value = "selectedFolderId", required = false) Long selectedFolderId, 
      @RequestParam(value = "selectedDocId", required = false) Long selectedDocId, HttpServletRequest request, HttpServletResponse response) throws Exception { 
     // Generates the page attributes 
     ModelAndView modelAndView = new ModelAndView(); 
     modelAndView.setViewName(FOLDER_FORM_VIEW); 

     return modelAndView; 
    } 

    @RequestMapping(value = UPLOAD_FILES, method = RequestMethod.POST) 
    public ModelAndView postUploadFile(HttpServletRequest request, @ModelAttribute("uploadItem") UploadItem upitem, @ModelAttribute("selectedFolder") FolderForm root, BindingResult result, 
      SessionStatus status, ModelAndView modelAndView) { 
     // Process upitem 
     Log.debug("INIT"); 
    } 

} 

UploadItem是具有CommonsMultipartFile(org.springframework.web.multipart.commons)內的對象。

它通常工作正常。無論文件大小(甚至800MB),也不需要處理時間。然而,也有一些時候,用戶越來越以下異常:

org.springframework.web.HttpSessionRequiredException: Expected session attribute 'selectedFolder' 
    at org.springframework.web.method.annotation.ModelFactory.initModel(ModelFactory.java:103) 
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:614) 
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578) 
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80) 
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923) 
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852) 
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882) 
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:637) 
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330) 
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptorFilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118) 
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.authentication.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:150) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:183) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87) 
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342) 
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192) 
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160) 
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) 
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259) 
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235) 
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) 
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) 
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) 
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) 
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103) 
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) 
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293) 
    at org.apache.coyote.ajp.AjpAprProcessor.process(AjpAprProcessor.java:448) 
    at org.apache.coyote.ajp.AjpAprProtocol$AjpConnectionHandler.process(AjpAprProtocol.java:403) 
    at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1703) 
    at java.lang.Thread.run(Thread.java:662) 

此異常由這種方法逮住:

@ExceptionHandler({ Exception.class}) 
public ModelAndView handleExceptionArray(Exception ex) { 
    Log.error("Recogida excepcion " + ex.getClass().getSimpleName(), ex); 
    String errorMessage = "Internal error"; 
    ModelAndView modelAndView = new ModelAndView(); 
    // modelAndView.setViewName("error/error"); 
    modelAndView.addObject("name", ""); 
    modelAndView.addObject("exception", errorMessage); 

    modelAndView.setViewName("redirect:" + FolderFormController.getRedirectUrl()); 
    saveError(modelAndView, errorMessage); 
    return modelAndView; 
} 

此外,不打印日誌「INIT」,所以它沒有進入控制器方法。

發生了什麼事?而且,更重要的是,我怎樣才能避免這種情況?有任何想法嗎?

更新:造成

嗯,看來原因是用戶會話過期。 Tomcat的到期時間爲30分鐘。如果上傳比這更多,那麼它不會到達我的控制器。有什麼辦法可以避免會話的丟失?

+0

由於此問題是間歇性的並且存在HttpSessionRequiredException,因此會話到期時間是可疑的(假設尚未調用SessionStatus.setComplete())。避免這種情況的一個解決方案是在客戶端保留selectedFolder(使用隱藏字段)並在上傳文件時發佈這些字段。 –

+0

selectedFolder從具有此屬性的表單發佈到postUploadFile。另外,如何上傳文件時會話過期?溝通不停止,是嗎? – Goyo

回答

1

你已經發現它必須是會話超時問題,很好。如果沒有新的請求在上傳開始後觸發特定的會話,則它被認爲是「無活動」,這在您的情況下不正確。

有幾種方法來解決這個:

的web.xml

增加web.xml超時,Tomcat中的默認值是30' (也就是說,如果未在指定web.xml)。

<session-config> 
    <session-timeout>45</session-timeout> 
</session-config> 

但是,這可能不是所希望的,因爲根據經驗,超時應該是「越低越好」 - 無論如何。我們通常使用5'。

alter session的

您可以設置一般會話超時時間爲5' ,但會增加一次上傳開始使用HttpSession#setMaxInactiveInterval

指定的時間(秒),之前在客戶機請求之間 servlet容器將使此會話失效。否定時間 指示會話永遠不應超時。

但是,如果你的控制器沒有被調用,這將不得不在Spring之前完成。最佳人選是ServletFilter

AJAX

開始從上傳提交剛過客戶對心臟跳動的要求。然後在循環中重複心跳。如果會話超時時間爲5',則可以使用4'的心跳間隔。