這兩天在實現一個批處理操作,但是想讓前台實時顯示后台批處理進度,本想着用復雜一些的框架可以實現異步信息調用
但是鑒於是內部管理系統,且只有一兩個人用到這個功能,所以做了一個簡單的長連接方式的實時響應
有些人說使用ajax請求,定時刷新,個人覺得很耗服務器資源,而且准確的說也不是實時的
【原文摘自 BearRui(AK-47) 的Blog】http://www.blogjava.net/BearRui/archive/2010/06/01/ajax_better_experience.html
使用技術:AJAX - onreadystatechange 事件
詳細解釋:http://www.runoob.com/ajax/ajax-xmlhttprequest-onreadystatechange.html
MSDN:https://msdn.microsoft.com/en-us/library/ms535874(VS.85).aspx
要解決實現上面的功能,需要解決下面幾個問題:
1. 服務器如何在處理一部分數據后傳遞部分response到瀏覽器。
2、瀏覽器如何能處理服務器傳遞過來部分數據,並保持http連接直到處理完全完畢。
解決
第1個問題,使用flush讓response分塊進行呈現就可以了,具體請參考我另一遍隨筆"flush讓頁面分塊,逐步呈現 http://www.blogjava.net/BearRui/archive/2010/05/19/flush_chunk_encoding.html";
第2個問題,則需要用到XMLHttpRequest的readyState狀態,w3c對 readyState 定義如下幾個值:
UNSENT = 0; // 沒有發送請求
OPENED = 1; // 已經打開http連接
HEADERS_RECEIVED = 2; // 接收到response header
LOADING = 3; // 真正接收response body
DONE = 4; // 請求接收完畢
另外的問題: readyState每次返回0-4的狀態時,state的值是多少呢?200嗎?還是其他?
首先,status是XMLHttpRequest對象的一個屬性,表示響應的HTTP狀態碼。
在HTTP1.1協議下,HTTP狀態碼總共可分為5大類,如下表所示:
1XX 服務器收到請求,需要繼續處理。例如101狀態碼,表示服務器將通知客戶端使用更高版本的HTTP協議。
2XX 請求成功。例如200狀態碼,表示請求所希望的響應頭或數據體將隨此響應返回。
3XX 重定向。例如302狀態碼,表示臨時重定向,請求將包含一個新的URL地址,客戶端將對新的地址進行GET請求。
4XX 客戶端錯誤。例如404狀態碼,表示客戶端請求的資源不存在。
5XX 服務器錯誤。例如500狀態碼,表示服務器遇到了一個未曾預料的情況,導致了它無法完成響應,一般來說,這個問題會在程序代碼出錯時出現。
其次,經過測試,每次readyState有狀態變化,如果后台不異常,那么返回都是200,所以當readyState返回4時候,需要對state=200做判斷,否則提示批處理異常
下面來看代碼
===================前端代碼==================== //打開框 $('#settle-batch').dialog("center"); settleBatch.html(""); $('#settle-batch').dialog('open').dialog('setTitle', "批量處理進度顯示"); //發送批處理請求 var xhr = new window.XMLHttpRequest(); if(!window.XMLHttpRequest){ try { xhr = new window.ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { alertMsg("失敗", "創建請求失敗,請重試"); } } xhr.open("post","http://localhost:8081/WMS/agentsettle/batch"); settleBatch.append("<p>正在發送批處理請求 ...</p>"); //處理實時回調信息 var oldSize=0; xhr.onreadystatechange = function(){ var readyState = xhr.readyState; //成功發送請求 if(readyState == 2){ settleBatch.append("<p>啟動上月代理分成批處理</p>"); settleBatch.append("<p style='margin: 10px;'></p>"); } //接收事實數據 if(readyState > 2){ var tmpText = xhr.responseText.substring(oldSize); oldSize = xhr.responseText.length; if(tmpText.length > 0 ){ // 設置文本 var str = tmpText.replace(/"/g, ""); settleBatch.append("<p>" + str + "</p>"); } } //處理成功 if(readyState == 4){ settleBatch.append("<p style='margin: 10px;'></p>"); if (xhr.status === 200) { settleBatch.append("<p>批處理完成 !!!</p>"); } else { settleBatch.append("<p>批處理異常結束 !!!???</p>"); } } } xhr.send(null);
=============后台代碼================== response.setHeader("Content-Type","application/x-javascript"); //設置out的字符編碼,否則到前台可能中文亂碼 response.setCharacterEncoding("UTF-8"); PrintWriter out = getResponse().getWriter(); Thread.sleep(1000); out.println("正在處理第1個 ..."); out.flush(); Thread.sleep(1000); out.println("正在處理第2個 ..."); out.flush(); Thread.sleep(1000); out.println("正在處理第3個 ..."); out.flush(); Thread.sleep(1000); out.println("正在處理第4個 ...");
經測試,現在chrome、firefox、360瀏覽器都支持readyState=3的處理
基於Webkit的瀏覽器支持的不是很好,需要設置Content-Type:application/x-javascript才行(經測試發現Content-Type:text/html在有些情況下正常,有些情況下又不正常,而用application/x-javascript都正常)。
另外方法:
1、jquery 1.5已經支持onreadysatechange,可以使用
2、有些 消息異步通訊 的框架也可以實現這樣的功能,不過要看實現實時顯示的功能復雜度