老生常談:struct2文件下載(帶點擊取消出現的異常解決辦法)

       網上關於Struts2文件下載的文章太多了,但對於新手來說,要查閱到使用完整的Demo卻是非常困難,或者說實現“成功”下載後便置之不理了,但當項目投入生產環境後,出了問題卻無法解決,筆者根據自己的經驗,下面介紹較爲完整的可用於實際項目的Struts2文件下載Demo。

       1.Struts文件下載常見配置(基礎配置)

           <!-- 處理文件下載請求的action -->
           <action name="fileDownload" class="my.pro.FileDownloadAction">
               <!-- 對於文件下載,這裏的type必須是stream -->
            <result type="stream" name="downLoad">
                <!-- 文檔類型,下面這種是通用類型,適用於任何文檔類型 -->
                  <param name="contentType">application/octet-stream</param>
                  <!-- 輸入流名稱  類my.pro.DownLoadFile中與之對應的方法爲getTarget(),且返回值爲InputStream -->
                  <param name="inputName">targetFile</param>
                  <!-- 下載時的文件名 -->
                  <param name="contentDisposition">attachment;filename="${fileName}"</param>
                  <!-- 緩衝區大小 -->
                  <param name="bufferSize">4096</param>
           </result>
        </action>


       2.下載類FileDownloadAction的寫法

public class FileDownloadAction  extends ActionSupport{
	private static final long serialVersionUID = 1L;
	//該屬性是依賴注入的屬性,該屬性可以在配置文件中動態指定該屬性值 
	private String fileName; 
	//依賴注入該屬性值的setter方法 
	public void setFileName(String fileName) {
		this.fileName = fileName;
	} 
	/* 
	下載用的Action應該返回一個InputStream實例, 
	該方法對應在result裏的inputName屬性值爲targetFile 
	*/ 

	public InputStream getTargetFile() throws Exception 
	{ 
		/*可以放入業務邏輯處理*/
		return ServletActionContext.getServletContext().getResourceAsStream(fileName); 
                 
	} 
	//處理用戶請求的execute方法,該方法返回success字符串 
	@Override
	public String execute() throws Exception 
	{ 
		return SUCCESS; 
	}
	public String getFileName() {
		return fileName;
	}
}

       這裏得介紹下ServletActionContext.getServletContext().getResourceAsStream( )了,這裏用不用此方法得取決了你的文件存放位置,這裏不是指絕對路徑,而是相對於classpath環境變量的相對路徑。在開發環境下,一般src目錄包含在classpath下的,在tomcat下,一般指向WEB-INF/classes目錄,不過通常情況下,開發者們都不想將待下載的文檔置於網站目錄下,而是放在服務器的某個路徑下便於管理,這樣就必須在tomcat中設置context參數指向這個路徑。如果讀者不想這樣做,而是想在下載時直接打開服務器上的文件絕對路徑,就必須換個方法了,如下:

public InputStream getTargetFile() throws Exception 
	{ 
		/*可以放入業務邏輯處理*/
                File file = new File("文件絕對路徑");
                /*這裏可考慮算法的健壯性,如文件不存在時的處理,當然用try catch了*/
                return  new FileInputStream(file);              
	} 


        通過上面的介紹,讀者可以實現的下載了,當然很多初學者弄到這步也就沾沾自喜了。不過等待着的將是一個讓人頭疼的異常,有人爲此奮戰許久。那是什麼異常呢,先賣個關子。我們先說一個用戶行爲,當我們在下載文件時,如果發現網速太慢或是其他情況,通常會取消下載。下面要說的就是這個問題,用Struts2實現文件下載時點擊取消帶來的異常(保存或打開不會出現),如下:

嚴重: Servlet.service() for servlet default threw exception  
java.lang.IllegalStateException  
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:407)  
at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:108)  
at com.opensymphony.module.sitemesh.filter.PageResponseWrapper.sendError(PageResponseWrapper.java:176) 

或者:Broken pipe

       爲什麼會出現此異常呢?下面講講原理(理解原理,對於解決問題是必要的),在Struts2的配置文件中,對於下載的配置參數用的是stream,stream對應的類是org.apache.struts2.dispatcher.StreamResult,其處理過程是:

       (1)配置其中result標籤下的各個參,如:

                  <param name="contentType">application/octet-stream</param>
                  <!-- 輸入流名稱  類my.pro.DownLoadFile中與之對應的方法爲getTarget(),且返回值爲InputStream -->
                  <param name="inputName">targetFile</param>
                  <!-- 下載時的文件名 -->
                  <param name="contentDisposition">attachment;filename="${fileName}"</param>
                  <!-- 緩衝區大小 -->
                  <param name="bufferSize">4096</param>
       (2)當請求文件下載後,服務器響應後開始獲取輸入流,並同時與客戶端建立輸出流,服務器與客戶端鏈接通過Socket進行連接。

       (3)數據開始傳輸。在傳輸的過程中,如果點擊了“取消”,代表關閉了所有的流,但實際上Socket沒有斷開,就是流還沒有關閉,所以在JSP容器通過Response獲取輸出流之前,前面的流並沒有關閉,故而出現異常。

       下面給出解決辦法。

       3. Struts2下載文件點擊取消出現的異常解決辦法

         (1)逃避辦法

       這是一種異常攔截方法,當發生ClientAbortException異常時,跳轉到指定的頁面,而這個頁面什麼都不用輸出,實際上異常依然發生,只不過不顯示,當看不見,有點“掩耳盜鈴”的意思,所以叫“逃避辦法”,筆者不推薦這個方,畢竟治標不治本。示例如下:

<package name="default" extends="struts-default">
     <global-results>
        <result name="client-abort-exception">err.jsp</result>
     </global-results>
</package>
<package name="mypck" extends="struts-default">
     <exception-mapping result="client-abort-exception" exception="org.apache.catalina.connector.ClientAbortException"/>
     <action name="download" class="my.pro.FileDownloadAction">
       <result name="success" type="stream">
         <para mname="inputName">targetFile</param>
         <param name="contentDisposition">filename=""</param>
         <param name="buffersize">4096</param>
       </result>
     </action>
</package>
        (2)根治方法

        前面分析了tream對應的類是org.apache.struts2.dispatcher.StreamResult的執行過程,要徹底解決這個問題,還需從它入手。如果將其換成能代替的類,並且這個類不會出現這個異,原因是點擊“取消”的同時關閉流,不會再報出該異常,就可以根治了。這裏用struts2-sunspoter-stream-1.0.jar代替之,注意是1.0。方法如下:

         第一步:先下載struts2-sunspoter-stream-1.0.jar,並複製到項目的/WEB-INF/lib下。下載地址爲:http://download.csdn.net/detail/xiangchengguan/8010633

         第二步:在原有的struts.xml的基礎上進行相應的配置,先在〈package〉包的第一個節點前(否則會出錯)插入<result-types>,如下:

<package name="default" namespace="/"extends="struts-default">  
    <!-- 添加如下內容 -->  
    <result-types>  
       <result-type  name="streamx"class="com.sunspoter.lib.web.struts2.dispatcher.StreamResultX"/>  
    </result-types>
    ……
</package>

        接下來就是將下載類配置中的stream換成streamx啦,共他不變,如下:

           <!-- 處理文件下載請求的action -->
           <action name="fileDownload" class="my.pro.FileDownloadAction">
               <!-- 對於文件下載,這裏的type必須是stream -->
            <result type="streamx" name="downLoad">
                <!-- 文檔類型,下面這種是通用類型,適用於任何文檔類型 -->
                  <param name="contentType">application/octet-stream</param>
                  <!-- 輸入流名稱  類my.pro.DownLoadFile中與之對應的方法爲getTarget(),且返回值爲InputStream -->
                  <param name="inputName">targetFile</param>
                  <!-- 下載時的文件名 -->
                  <param name="contentDisposition">attachment;filename="${fileName}"</param>
                  <!-- 緩衝區大小 -->
                  <param name="bufferSize">4096</param>
           </result>
        </action>
     這樣就算大功告成了,點擊“取消”的同時也關閉了流,不會再報出該異常。如果配置了log4j.properties,當用戶點擊“取消”時,將出現如下警告,Socket非正常中斷,但流已經關閉。如下:

    WARN StreamResult:45 - StreamResultX Warn : socket write error






      






   







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章