2009年7月26日 星期日

ajax file upload monitor struts2 fix

沒有留言:
最近試玩著plurk後,原本應該來寫篇plurk api java版的心得,但想想Plurk api已經氾濫了,也就算了。而大部分就是揣測http request接收何種網址與參數,這些在參考php api都可以做出來,實際上參考jPlurk的寫作,就可把剩下來的methods依樣畫葫蘆改成Java版的,也就差不多了完成了。

說到這邊來點有趣的東西吧,其實想說也就是 Monitored Ajax File Upload - For Struts 2。
其實在Java的Web App中寫些upload的功能是很基本的需求,早已存在各種fileupload套件,不得不提到的,apache的commons project對java真的貢獻良多。他們當也提供fileupload的處理。這對我們的好處是既然使用struts2當作是web framework,使用apache提供的自然不在話下。

其實要講的就是能夠使用ajax的fileupload方式,並且能夠即時更新上載的進度。參考demo網站。可以在該網站試著丟上任意檔案看看效果,該網站會自動把上載的檔案刪掉。本文章可算是該網站的延伸閱讀。使用ajax file upload for struts2 套件,可在google code下載AjaxFileUpload-0.03.zip。上面也有0.04版,但是卻是用jdk6版本包的,一般使用5.0開發環境會出現Java版本號碼錯誤。

一般開發者在看過他的簡單使用說明後,應該是可以正常使用的,但是結果並不怎麼如意。因為其中有幾個隱藏問題沒有解決。到頭來是為了解決小小簡單的問題,反而挖了該套件裡面的程式來看。

設定步驟:
1.當然是下載該套件0.03版,並且擁有struts2的開發環境。

2.在你的jsp頁面,加上該fileupload tag
<%@ taglib uri="http://www.davidjc.com/taglibs" prefix="djc" %>
<head>
...
<djc:head />
...
</head>
<body>
...
<djc:ajaxfileuploadform action="demo" dobefore="" doafter="" />
...
</body>


3. 設定strust.xml
<package name="ajaxfileupload" namespace="/" extends="ajaxfileupload-default">
<action name="demo" class="com.davidjc.ajaxfileupload.action.Demo">
<interceptor-ref name="fileUploadStack" />

<result name="success" type="httpheader">
<param name="status">200</param>
</result>
</action>
</package>



4. 撰寫pojo action Extend com.davidjc.ajaxfileupload.action.FileUpload

package com.davidjc.ajaxfileupload.action;

import java.io.File;
import org.apache.log4j.Logger;
import com.opensymphony.xwork2.Action;

public class Demo extends FileUpload{

private final Logger logger = Logger.getLogger(Demo.class);

public String execute() {
File uploadedFile = this.getUpload();
String contentType = this.getUploadContentType();
String fileName = this.getUploadFileName();


return Action.SUCCESS;
}

}


但是簡單的設定完你會發現不work,跳出一個錯誤訊息

沒錯,是個javascript錯誤。逼不得以只好從AjaxFileUpload-0.03.jar package中挖掘這是哪來的script,沒錯。
在裡面中的一隻ajaxFileUploadProgress.js中,找到

/**
* Gets the upload progress from the server and then recursively calls itself until
* the upload is complete
*/

function getProgress() {
jQuery.ajax({
type: "POST",
url: "uploadprogress.action?rnd=" + new Date().getTime(),
dataType: "json",
success: function(jsonData) {
....

},
error: function(request, error, exception) {
alert("Error detected: " + error + " and exception " + exception);
}
});
}


也就是說他在這段呼叫ajax的請求回傳錯誤了。這也是說要來檢查一下呼叫這隻action時,在不使用ajax時結果是如何。這邊的麻煩是該action是包在class中,需要使用log4j來開啟原本程式中的debug log,但結果在需要知道程式跑到的地方沒有寫log訊息。這也是一常常在debug別人套件的苦惱。要是這個套件寫的沒很好,又exception沒有拋出、log也沒寫到這就慘了。還好現在大家都是用google code 去maintain很自然的,也懶得裝svn就上google code去browser了,直接把code抓回來貼上去跑。關鍵的地方是還得解開AjaxFileUpload-0.03.jar 中的struts-plugin.xml去看實際的action是哪程式,原來是位於com.davidjc.ajaxfileupload.action中的UploadProgress我們可以看到。

當部屬好的時候我們直接去run該action,並觀察已經修改程式的log顯示,最後發現程式都跑到取得upload file info後要塞到json並return時某行就沒有往下跑。很納悶一定是哪邊有exception沒有throws或是print出來。這也是跟他使用json-lib有關。在發現直接catch該段程式碼無效後,只好一行一行插棋子找,最終確定是發生在塞資料在json object的地方有問題。只好就來研究json-lib這個套件了,在官方網站看到原來他有相依的package要使用
Json-lib requires (at least) the following dependencies in your classpath:

* jakarta commons-lang 2.4
* jakarta commons-beanutils 1.7.0
* jakarta commons-collections 3.2
* jakarta commons-logging 1.1.1
* ezmorph 1.0.6

最終則發現少了ezmorph的package...

後記:
1. 原作者少提到的一個相依套件
2. json-lib在缺少某個package時,竟然沒有拋出ClassNotFound之類的exception 這點很奇怪,可能是舊版的json-lib問題。
3. 在解決跑progressAction時,也發現struts config xml沒有設定好,故讀不到該package的action
4. 另外還有把json回傳成text string時遭遇到小困難,因為包在AjaxFileUpload-0.03.jar有寫一個return type來回傳json type 成 text string,但是他的程式出現NoSuchMethod Exception,可能是我用struts2 2.1.6版原因,不過從google code重抓上邊新的code貼回去後就好了。
5. 過程中有參考一個討論範例會比較清楚,他們也是用同個套件,遇到類似的問題
http://www.struts2.org/ajax-file-upload-in-struts2-using-ajax-file-upload-plugin/