[Struts2] 用downloadAction解決IE開啟檔案連結時log會記錄error的問題
環境:Struts2.3.20 + Internet Exploer 11
程式:連結到專案內檔案的超連結
例如 <a href="file/template.xls">範本</a> 連結到 src/main/webapp/file 下的 template.xls
問題:用IE點選檔案「連結>開啟 (Excel/Word檔)」時,error log會列印出大量錯誤訊息
(可以正常下載並開啟檔案,使用者側感受不到錯誤,但背後的log會記錄錯誤)
(選「儲存」或「另存新檔」選項都沒問題,只有「開啟」的選項會出現錯誤)
(Firefox和Chrome不會發生這個問題,只有IE會)
錯誤訊息:
Could not find action or result: /project/file/
There is no Action mapped for action name file/. - [unknown location]
at com.opensymphony.xwork2.DefaultActionProxy.prepare(DefaultActionProxy.java:185)
at org.apache.struts2.impl.StrutsActionProxy.prepare(StrutsActionProxy.java:64)
(以下省略)
看起來就是他把超連結當成struts2的action去解讀了,所以找不到mapping
但很神奇的就是要在IE+開啟的情況下才會發生,而且他會執行很多次 (出現好幾個同樣的長串錯誤訊息)
師傅說大概是IE的bug ... IE你可不可以問題不要那麼多!!!
理論上是個換瀏覽器就能解決的問題,但好無奈實際上大部分User都還是用IE
他們根本不知道也不想知道"瀏覽器"是蝦米碗糕,無論怎樣宣導都不想換掉習慣的工具
解決方法:用downloadAction取代檔案的直接連結
一個既然你一定得把它當action那就做個action給你辨識吧的概念
jsp 裡改成連結到action,檔名當參數傳進去
<s:a action="download_downloadSystemFile"><s:param name="fileName">template.xls</s:param>範本</s:a>
DownloadAction.java 寫個對應的function
//import省略
public class DownloadAction extends ActionSupport {
private InputStream inputStream;
private String fileName;
public String downloadSystemFile(){
try{
fileName=fileName;
inputStream=this.getClass().getResourceAsStream("../../../../../file/"+fileName);
}catch(Exception e){
log.error("error",e);
}
return "file";
}
//inputStream和fileName的getter/setter省略
}
說明一下,那個 "../../../../../file/" 是相對路徑,假設package是aaa.bbb.ccc
class檔位於 project\target\classes\aaa\bbb\ccc
file檔位於 project\src\main\webapp\file,相對路徑就會變成這樣
然後return 的 "file" 對應,要去 struts.xml 加一個result type,重點是要設定成stream result
<result name="file" type="stream">
<param name="contentDisposition">attachment;filename="${fileName}"</param>
</result>
如果確定是某種檔案可以再加 param
例如指定用Excel開啟 <param name="contentType">application/vnd.ms-excel</param>
這樣改使用者不會感受到有變化,而背後log不會再出現那個錯誤訊息 (雖然感覺工程有點大)
2016-06-20更新
雖然上面那個問題解決了,可是發現用IE6/8下載會出現另一個錯誤… …
觀察使用者的錯誤log發現一直以來常出現的這個錯誤只有在使用者用 IE6/8下載系統檔案的時候會出現
com.opensymphony.xwork2.util.logging.commons.CommonsLogger.error(CommonsLogger.java:42) Exception occurred during processing request: ServletException including path 'layout.jsp'.
org.apache.tiles.impl.CannotRenderException: ServletException including path '/layout.jsp'.
at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:692)
at org.apache.tiles.impl.BasicTilesContainer.render(BasicTilesContainer.java:644)
(中略)
Caused by: org.apache.tiles.util.TilesIOException: ServletException including path '/layout.jsp'.
at org.apache.tiles.servlet.context.ServletUtil.wrapServletException(ServletUtil.java:241)
at org.apache.tiles.servlet.context.ServletTilesRequestContext.include(ServletTilesRequestContext.java:262)
(中略)
Caused by: java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:564)
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:212)
其中那個 'layout.jsp' 是tiles的layout檔
自己電腦沒有IE6/8沒辦法測試,只知道IE11和chrome/FF一切正常
也不清楚使用者端有沒有下載成功、或有沒有看見什麼錯誤,目前無解