struts2文件下载,解决jCannot call sendError() after the response has been committed的问题

出现这个错误的原因是当在页面点击一次下载时,向服务器发送了两次请求,所以虽然可以成功下载,但后台会报错。通过调试发现确实两次访问了download()方法,为了看的更清楚我加了调试语句错误信息如图:

Hibernate: select resource0_.id as id1_0_, resource0_.saveName as saveName2_0_, resource0_.fileName as fileName3_0_, resource0_.description as descript4_0_, resource0_.uploadTime as uploadTi5_0_ from t_resource resource0_ where resource0_.id=?
访问了一次download
访问了一次getResourceFile
访问了一次getDownloadFileName()
五月 27, 2015 9:11:24 下午 org.apache.catalina.core.StandardWrapperValve invoke
严重: Servlet.service() for servlet [default] in context with path [/blog] threw exception
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
    at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:462)
    at org.apache.struts2.dispatcher.DefaultDispatcherErrorHandler.handleErrorInDevMode(DefaultDispatcherErrorHandler.java:109)
    at org.apache.struts2.dispatcher.DefaultDispatcherErrorHandler.handleError(DefaultDispatcherErrorHandler.java:57)
    at org.apache.struts2.dispatcher.Dispatcher.sendError(Dispatcher.java:909)
    at org.apache.struts2.dispatcher.Dispatcher.serviceAction(Dispatcher.java:576)
    at org.apache.struts2.dispatcher.ng.ExecuteOperations.executeAction(ExecuteOperations.java:81)
    at org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter.doFilter(StrutsPrepareAndExecuteFilter.java:99)
    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:537)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1085)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1556)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1513)
    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)

Hibernate: select resource0_.id as id1_0_, resource0_.saveName as saveName2_0_, resource0_.fileName as fileName3_0_, resource0_.description as descript4_0_, resource0_.uploadTime as uploadTi5_0_ from t_resource resource0_ where resource0_.id=?
访问了一次download
访问了一次getResourceFile
访问了一次getDownloadFileName()

如图所示可以清楚的看到在第一次访问下载方法成功后又访问了一次下载方法。至于为什么这样,从我的代码里我现在还没有发现原因,暂时还是觉得是struts2的一个bug吧,不然也不会有我后面的解决方案了。
解决方案有两种:
1.struts2框架+servlet api
在action中获得response,然后直接通过设置response头来输出文件流,也就是直接用servlet下载,但返回值要写成return null,这样就不交给struts2管理。 (其实这样还更方便,不过servlet的下载方式我就不讲了,主要是解决struts2的这个问题)

2.利用struts2-sunspoter-stream-1.2.jar包强制关闭socket流
如果非得要用struts2来下载(太年轻!!),可以借助struts2-sunspoter-stream-1.2.jar包来解决。
使用如下:
*下载struts2-sunspoter-stream-1.2.jar,添加到lib目录下(支持jdk1.6及以上,jdk1.6以下的版本请下载struts2-sunspoter-stream-1.0.jar)
*修改struts.xml:

在package标签下添加
<result-types> 
    <result-type name="streamx" class="cc.fozone.struts2.StreamResultX"/> 
</result-types>  

将result的type="stream" 改为 type="streamx"

如下所示:

<action name="resourceMgr_*" class="com.szy.blog.action.ResourceManagerAction" method="{1}">
        <result name="list">/WEB-INF/jsp/resourceMgrAction/list.jsp</result>
        <result name="toList" type="redirectAction">resourceMgr_list</result>

        <!--文件下载-->
        <result name="download" type="streamx">
            <!--application/octet-stream表示任意类型;一定要加charset,不然也不能正确的显示文件名(不管是中文名还英文名);或者也可以设置为application/x-download(个别浏览器不能正确显示)-->
            <param name="contentType">application/octet-stream;charset=ISO8859-1</param> 
            <!--指定下载文件的入口输入流,即方法名去掉get后首字母小写-->
            <param name="inputName">resourceFile</param> 
            <!--attachment以附件方式下载,默认是inline方式即下载完后在浏览器显示-->
            <param name="contentDisposition">attachment;filename=${downloadFileName}</param> <!--指定下载的文件名-->
            <param name="bufferSize">4096</param> <!--指定下载文件的缓冲大小-->
        </result>

    </action>   

注意这里还是会两次访问下载方法,也就是还是会两次访问数据库,只是不报错而已。
修改后的后台显示如下:

Hibernate: select resource0_.id as id1_0_, resource0_.saveName as saveName2_0_, resource0_.fileName as fileName3_0_, resource0_.description as descript4_0_, resource0_.uploadTime as uploadTi5_0_ from t_resource resource0_ where resource0_.id=?
访问了一次download
访问了一次getResourceFile
访问了一次getDownloadFileName()
2015-05-27 21:36:21,591 WARN [org.apache.struts2.dispatcher.StreamResult] - StreamResultX Warn : socket write error
Hibernate: select resource0_.id as id1_0_, resource0_.saveName as saveName2_0_, resource0_.fileName as fileName3_0_, resource0_.description as descript4_0_, resource0_.uploadTime as uploadTi5_0_ from t_resource resource0_ where resource0_.id=?
访问了一次download
访问了一次getResourceFile
访问了一次getDownloadFileName()

struts2-sunspoter-stream-1.2.jar的使用可参看:http://www.oschina.net/p/streamresultx

 

你可能感兴趣的:(response)