ajax|js|效能
四、處理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用於實現進階使用者介面組件的可能性方面引發你的進一步思考。毫無疑問,本文中的方案比較冗長並且有可能得到進一步的改進。我希望你能詳細地分析一下本文中所提供的完整的原始碼來深入理解本文中所討論的概念。