Struts2 下载点取消报异常最终解决办法

2017-08-02  本文已影响0人  0xkevin

在我们做struts2文件下载的时候,经常会遇到这种问题:点“打开/保存”一切正常,但当我们点击“取消”时,却报一堆的异常,非常让人头疼,错误如下(每个人的错误估计不太一样,以我的为例):

2011-5-19 10:30:23 org.apache.catalina.core.ApplicationDispatcher invoke  
严重: Servlet.service() for servlet jsp threw exception  
java.lang.IllegalStateException: getOutputStream() has already been called for this response  
    at org.apache.catalina.connector.Response.getWriter(Response.java:611)  
    at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:198)  
    at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:112)  
    at javax.servlet.ServletResponseWrapper.getWriter(ServletResponseWrapper.java:112)  
    at org.apache.jasper.runtime.JspWriterImpl.initOut(JspWriterImpl.java:125)  
    at org.apache.jasper.runtime.JspWriterImpl.flushBuffer(JspWriterImpl.java:118)  
    at org.apache.jasper.runtime.PageContextImpl.release(PageContextImpl.java:180)  
    at org.apache.jasper.runtime.JspFactoryImpl.internalReleasePageContext(JspFactoryImpl.java:118)  
    at org.apache.jasper.runtime.JspFactoryImpl.releasePageContext(JspFactoryImpl.java:77)  
    at org.apache.jsp.layout.adminlayout_jsp._jspService(adminlayout_jsp.java:204)  
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)  
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)  
    at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:377)  
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:313)  
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:260)  
    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.apache.struts2.dispatcher.FilterDispatcher.doFilter(FilterDispatcher.java:389)  
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)  
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)  
    at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:39)  
    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.ApplicationDispatcher.invoke(ApplicationDispatcher.java:646)  
    at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:551)  
    at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:488)  
    at com.opensymphony.module.sitemesh.filter.PageFilter.writeDecorator(PageFilter.java:173)  
    at com.opensymphony.module.sitemesh.filter.PageFilter.applyDecorator(PageFilter.java:158)  
    at com.opensymphony.module.sitemesh.filter.PageFilter.doFilter(PageFilter.java:62)  
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)  
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)  
    at org.apache.struts2.dispatcher.ActionContextCleanUp.doFilter(ActionContextCleanUp.java:102)  
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)  
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)  
    at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:164)  
    at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:141)  
    at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:90)  
    at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:417)  
    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:102)  
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)  
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)  
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:857)  
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:588)  
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)  
    at java.lang.Thread.run(Thread.java:619)  

异常原因分析:
[stream对应的类是org.apache.struts2.dispatcher.StreamResult,该类的处理过程如下:

这里要注意的是,但是实际发现Socket并没有断开!并且流也没有关闭!这一点非常重要!
所以在JSP容器通过Response获取输出流之前,前面的流并没有关闭,所以会造成该异常的报出。
我在CSDN上搜了一个人的解决方法,他是这么解决的:

<package name="default" extends="json-default" namespace="/">   
  <!-- 定义全局Result -->  
  <global-results>  
     <result name="client-abort-exception">/null.jsp</result>  
  </global-results>  
</package>  
  
<package name="main" extends="default" namespace="/">  
 <action name="download" class="fileAction" method="download">   
    <exception-mapping result="client-abort-exception" exception="org.apache.catalina.connector.ClientAbortException"></exception-mapping>  
    <param name="savePath">/upload/download</param>  
    <!-- 文件下载配置结果类型为stream的结果 -->  
    <result name="download" type="stream">  
      <param name="inputName">targetFile</param>   
      <!-- 指定保存还是直接打开要下载的文件默认为:直接打开,这里配置保存 -->  
      <param name="contentDisposition">attachment;filename="${downloadFileName}"</param>  
      <!-- 指定下载文件的缓存大小 -->  
      <param name="bufferSize">4096</param>  
    </result>  
 </action>  
</package>  

也就是说,如果抛出了ClientAbortException异常,那就跳转到“null.jsp”这个页面,这个页面中什么内容都没有。虽然这种方法暂时可行,但是当遇到其他异常的时候,也就是非ClientAbortException之后,这个方法就不可行了。我之前也是参照这种方法配置,但昨天我重新启动应用的时候,点击“取消”居然还报错,错误就是上面的一堆“java.lang.IllegalStateException”,而且这种方法也是一种逃避的方法,也就是置之不理。这并不可取,解决问题就要解决彻底明了,逃避是没用的,经过网上大师们的分析测试,最终解决办法如下:

<package name="main" extends="default" namespace="/">  
<!-- 添加如下内容 -->  
<result-types>  
<result-type  name="streamx" class="com.sunspoter.lib.web.struts2.dispatcher.StreamResultX"/>  
</result-types>  
  
  <action name="download" class="fileAction" method="download">  
    <param name="savePath">/upload/download</param>  
    <!-- 文件下载配置结果类型为streamx -->   
    <result name="download" type="streamx">  
      <param name="inputName">targetFile</param>   
      <!-- 指定保存还是直接打开要下载的文件默认为:直接打开,这里配置保存 -->  
      <param name="contentDisposition">attachment;filename="${downloadFileName}"</param>  
      <!-- 指定下载文件的缓存大小 -->  
      <param name="bufferSize">4096</param>  
    </result>  
  </action>  
</package>  

在这种方式下,只需添加一个result-type,将原有的result中type改为“streamx”,其他一律不变,在这种情况下,点击“取消”的同时也关闭了流,不会再报出该异常。
如果出现log4j的警告,比如:

21:23:44,676  WARN StreamResult:45 - StreamResultX Warn : socket write error  

出现该警告说明正确执行,该警告说明,Socket非正常中断,但是流确实已经关闭,自此再也不用看到上面出现的讨厌异常结果。


PS:struts2-sunspoter-stream-1.0.jar

上一篇下一篇

猜你喜欢

热点阅读