四、處理AJax請求
AJAX請求的生成是在這個組件的解碼方法中處理的。我們需要檢查這是否是一個實際的AJax請求(為了區別於正常的編譯行為),然後基於由ProgressMonitorFileItemFactory類的SessionUpdatingProgressObserver實例設置在會話中的值把一個XML響應發送回客戶端。
public void decode(FacesContext context, UIComponent component) {
UIFileUpload input = (UIFileUpload) component;
//檢查是否這是一個上傳進度請求,或是一個實際的上傳請求.
ExternalContext extContext = context.getExternalContext();
Map parameterMap = extContext.getRequestParameterMap();
String clientId = input.getClIEntId(context);
Map requestMap = extContext.getRequestParameterMap();
if(requestMap.get(clIEntId) == null){
return;//什麼也不做,返回
}
if(parameterMap.containsKey(PROGRESS_REQUEST_PARAM_NAME)){
//這是一個在該文件請求中的得到進度信息的請求.
//得到該進度信息並把它生成為XML
HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse();
//設置響應的頭信息
response.setContentType("text/XML");
response.setHeader("Cache-Control", "no-cache");
try {
ResponseWriter writer = FacesUtils.setupResponseWriter(context);
writer.startElement("progress", input);
writer.startElement("percentage", input);
//從會話中獲得當前進度百分數(由過濾器所設置).
Double progressCount = (Double)extContext.getSessionMap().
get("FileUpload.Progress." +input.getClIEntId(context));
if(progressCount != null){
writer.writeText(progressCount, null);
}else{
writer.writeText("1", null);//我們還沒有收到上傳
}
writer.endElement("percentage");
writer.startElement("clIEntId", input);
writer.writeText(input.getClIEntId(context), null);
writer.endElement("clIEntId");
writer.endElement("progress");
} catch(Exception e){
//做一些錯誤記錄...
}
}else{
//正常的譯碼請求.
...
五、 正常的譯碼行為 在正常的編譯期間,文件上傳生成器從請求屬性中檢索FileItem,正是在此處它被過濾器所設置,並且更新該組件的值綁定。然後,該會話中的進度被更新到100%,這樣在頁面上的JavaScript就可以把組件送入第3個階段。
//正常的譯碼請求.
if(requestMap.get(clIEntId).toString().equals("file")){
try{
HttpServletRequest request = (HttpServletRequest)extContext.getRequest();
FileItem fileData = (FileItem)request.getAttribute(clIEntId);
if(fileData != null) input.setSubmittedValue(fileData);
//現在我們需要清除與該項相關的任何進度
extContext.getSessionMap().put("FileUpload.Progress." + input.getClIEntId(context),new Double(100));
}catch(Exception e){
throw new RuntimeException("不能處理文件上傳" +" - 請配置過濾器.",e);
}
}
客戶端Javascript負責向服務器發出進度請求並通過不同階段來移動組件。為了簡化處理所有的浏覽器特定的XMLHttpRequest對象的問題,我選用了Matt Krause提供的AJaxRequest.JS庫。該庫最大限度地減少我們需要編寫的JavaScript代碼的數量,同時可以使這個組件正常工作。也許把這部分Javascript代碼打包為該組件的一部分,然後從PhaseListener生成它更好一些,但是,我已經通過定義一個到JSP頁面上的JavaScript庫的鏈接來盡力使得它簡單。
組件中的getProgressBarJavaScript方法被調用以生成JavaScript。使Javascript正常工作通常是實現AJax組件最困難的部分;不過我想,下面的代碼已經非常清晰易於理解了。盡管在我的示例中Javascript是嵌入到Java代碼中的,但是把它放到一個外部獨立的文件中也許更好一些。在本文中,我只是想使問題更為簡單些且只關心本文的主題。下面是一個將由組件生成的JavaScript的示例。其中假定,fileUpload1是被賦值到該文件組件的客戶端JSF Id,而uploadForm是Html表單的Id。
function refreshProgress(){
// 假定我們正在進入到階段2.
document.getElementById('fileUpload1_stage1').style.display = 'none';
document.getElementById('fileUpload1_stage2').style.display = '';
document.getElementById('fileUpload1_stage3').style.display = 'none';
//創建AJax寄送
AJaxRequest.post(
{
//指定正確的參數,以便
//該組件在服務器端被正確處理
'parameters':{ 'uploadForm':'uploadForm',
'fileUpload1':'fileUpload1',
'JSf.component.UIFileUpload':'1',
'AJax.abortPhase':'4' } //Abort at Phase 4.
//指定成功處理相應的回調方法.
,'onSuccess':function(req) {
var xml = req.responseXML;
if( XML.getElementsByTagName('clIEntId').length == 0) {
setTimeout('refreshProgress()',200); return;
}
var clIEntId = XML.getElementsByTagName('clIEntId');
clientId = clIEntId[0].firstChild.nodeValue + '_progressBar';
//從XML獲取百分比
var percentage = XML.getElementsByTagName('percentage')[0].firstChild.nodeValue;
var innerSpans = document.getElementById(clIEntId).getElementsByTagName('span');
document.getElementById(clIEntId + 'label').innerHtml = Math.round(percentage) + '%';
//基於當前進度,設置這些span的式樣類。
for(var i=0;i<innerSpans.length;i++){
if(i < percentage){
innerSpans[i].className = 'active';
}else{
innerSpans[i].className = 'passive';
}
}
//如果進度不是100,我們需要繼續查詢服務器以實現更新.
if(percentage != 100){
setTimeout('refreshProgress()',400);
} else {
//文件上傳已經完成,我們現在需要把該組件送入到第3個階段.
document.getElementById('fileUpload1_stage1').style.display = 'none';
document.getElementById('fileUpload1_stage2').style.display = 'none';
document.getElementById('fileUpload1_stage3').style.display = '';
}
}
});
}
return builder.toString();
六、 結論 我很希望,本文能夠在有關如何使得文件上傳更具有用戶友好性,並且把AJax和JavaServer Faces用於實現高級用戶接口組件的可能性方面引發你的進一步思考。毫無疑問,本文中的方案比較冗長並且有可能得到進一步的改進。我希望你能詳細地分析一下本文中所提供的完整的源代碼來深入理解本文中所討論的概念。