asp.net 后台多線程異步處理時的 進度條實現一(Ajax+Ashx實現以及封裝成控件的實現)


  (更新:有的同學說源代碼不想看,說明也不想看,只想要一個demo,這邊提供一下:http://url.cn/LPT50k (密碼:TPHU))

 

  工作好長時間了,這期間許多功能也寫成了不少的控件來使用,但是,都只是為了代碼的結構清析一些而已。而這一次,我決定完成一個我一直在網上尋找卻沒尋找到的功能。就是,在異步(比如說,后台的數據庫備份、后台的文件加解密這類操作)時,前台假死的情況。asp自帶了updatePanel,里面可以放一個自帶的progress控件,怎么說呢,這就是一個顯示而已,而且根本不能動。當你在備份數據的時候,你點備份,然后上面顯示“請等待....”,這叫progress,我擦。不過,微軟官方給了解釋了,說許多人習慣了觀察瀏覽器狀態欄上面的進度條,我想說那個條子好假,不信你試試。

 

  好了,下面來說一下我做的這個東西,當然,我先用ajax+ashx的方式想辦法讓它實現,下面是我設計前期自己畫的一張圖:

這張圖上面有錯誤,但是,作為我的第一個想法,我覺得它功不可沒,后面的圖都是在它的基礎上修改而來了,它也代表了我的初始想法,以及相關的知識殘缺。

 

  圖中已經可以把我大部分的想法表達出來了,當然會有人說,通常ajax輪詢進度都是這么實現的。但是,卻沒有人將它封裝成服務器控件。

  先說圖中明顯的錯誤:

    1、異步操作的時候,一旦請求終斷,線程則無法再訪問到session,這是一個致命的異常。因為,我本來打算以session來保存信息,並依靠session的機制來釋放我已經保存的內容,可以省下好多流程與精力,但事實證明,我還是太年輕了~~。所以,我改用了application,寫就了我的第一個后台ashx文件:

  

 1 public class AjaxAction : IHttpHandler, IReadOnlySessionState
 2     {
 3 
 4         public void ProcessRequest(HttpContext context)
 5         {
 6             context.Response.ContentType = "text/plain";
 7             if (context.Request.QueryString["Action"] != null)
 8             {
 9                 string command = context.Request.QueryString["Action"].ToString();
10                 System.Reflection.MethodInfo method = this.GetType().GetMethod(command);
11                 if (method != null)
12                 {
13                     method.Invoke(this, new object[] { context });
14                 }
15             }
16         }
17 
18         public void ProgressMothed(HttpContext context)
19         {
20             string guid = HttpContext.Current.Session.SessionID.ToString().Trim() + DateTime.Now.ToString("HHmmssffffff").Trim();
21             Thread th = new Thread(
22                 delegate()
23                 {
24                     for (int i = 0; i < 101; i++)
25                     {
26                         Thread.Sleep(100);
27                         context.Application[guid] = i;
28                         context.Response.Write(i.ToString());
29                     }
30                     
31                 }
32                 );
33             th.IsBackground = true;
34             try
35             {
36                 th.Start();
37                 context.Application.Add(guid, 0);
38                 context.Response.Write(guid);
39             }
40             catch (Exception ex)
41             {
42                 context.Response.Write("-1");
43             }
44         }
45         public void GetPercentMethod(HttpContext context)
46         {
47             try
48             {
49                 string guid = context.Request.QueryString["guid"].ToString();
50                 string per = context.Application[guid].ToString();
51                 context.Response.Write(per);
52             }
53             catch (Exception ex)
54             {
55                 context.Response.Write("-1");
56             }
57         }
58         public bool IsReusable
59         {
60             get
61             {
62                 return false;
63             }
64         }
 1 <script type="text/javascript"> 8     function AjaxMethod() {
 9         $.ajax({
10             url:location.href,
12             data: { Action: "ProgressMethod", ts: (new Date).getTime() },
13             success: function (data) {
14                 if (!(data == undefined || data == null || data == "")) {
15                     if (data != "-1") {
16                         AjaxGetPercent(data);
17                     } else {
18                         alert("此處理過程暫時無法連接,請稍后再試");
19                     }
20                 } else {
21                     alert("訪問的處理不存在,請刷新后重試["+data+"]");
22                 }
23             }
24         });
25     }
26     function AjaxGetPercent(guid) {
27         $.ajax({
28             url: location.href,
30             data: { Action: "GetPercentMethod",guid:guid, ts: (new Date).getTime() },
31             success: function (data) {
32                 if (data != "-1") {
33                     if (data < 100) {
34                         $("#progress_ensleep_pb").css("display", "block");
35                         $("#progress_ensleep_text").html(data);
36                         $("#progress_ensleep_pgress").css("width", data + "%");
37                         setTimeout(AjaxGetPercent(guid), 50);
38                     } else {
39                         $("#progress_ensleep_pgress").css("width", "100%");43                         $("#progress_ensleep_text").html(100);
45                     }
46                 } else {
47                     alert("操作過程中出現異常,請重試");
48                 }
49             }
50         });
51     }
52 </script>

  可以看出,我完全按照我當初的想法來做的,只是加了一些容錯機制,試驗了一下,一個進度條在一個頁面上,完成正常,並且加入了樣式,非常好看。但是,如果一個頁面上有十個怎么辦?這種情況很多,很常見。而且,每一個方法都要寫在ashx這個文件中,人家aspx.cs里面的東西憑什么往你ashx里面寫?這樣的結果當然就是,我的整個項目亂成面條(打了兩局dota的時間煮的面條)——看看不清,理理不起來。想做成引入型的,只能做成控件。

  但是,做成控件有以下幾個問題:

    1、一個頁面上要放n個控件,前台要為每個控件完成情況執行相應的js回調函數。

    2、這個控件正在執行的時候,如果頁面回傳,完了之后,它必須繼續跑,並且像沒回傳一樣。

    3、事件!!!!

  先說第三個,事件:

  因為控件的目的是執行異步操作,可以看見,我使用了子線程來處理,而子線程是由委托控件。一開始我是想,這是控件,我為什么放着事件不用呢?我把這個子線程要用到的方法寫成一個事件,由aspx.cs給這個事件寫方法體,不就可以了么?事實證明,我又秀了一次我的年輕~~~,ajax回傳的時候,根本就不會觸發控件的這一類事件,之所以說這一類,是因為像on_load()這些還是觸發的,但是,它是要處理管道管理的,而按鈕的onclick之類的無法觸發,因為ajax沒有帶viewstate過來。這就導致了,我的每一次ajax都不是postback,我了個擦啊~~0~~。然后我開始收集信息,就像三國殺逆風時你要數數剩余的牌,dota逆風時你要看一看地圖上每一個紅點閃現時它的裝備,war3時拼死一個步兵或者大g小g沖進對面看一看對面建築一樣……,然后我發現,我可以用的只有ajax帶一的context以及session這兩個東西,當然后台還有一個application,我不把它放入我的考慮范圍(你被虐慘了,你會想着你家還有一個牛逼的泉水么?)。然后我使用了委托,是的,寫了一個委托成員。然后爆露出一個成員方法以釋放被使用的application(編碼習慣良好,反正比南京的空氣要好得多~~~)。

  再說第第一個和第二個,

    js回調方法:我把這個拋給使用者寫了,放在aspx頁面,只要把函數設到控件的屬性里面就可以了,然后在控件生成的時候,將這個方法名寫到回調的地方。然后為控件生成的所有的js方法都起唯一的名字,即 將控件的ClientID放在方法的后面。這使得一個頁面上可以實現多個控件。

    回調后狀態的還原繼續:我在控件的On_load方法里面,每一次都查找看是否有可用的application,如果有,即會生成一個$(documeent).ready(function(){...}),里面會直接調用對percent(即當前進度)的ajax請求。

  下面上代碼:

 1 <%@ Control Language="C#" AutoEventWireup="true" CodeBehind="AjaxProgress.ascx.cs" Inherits="ESLib.Controls.AjaxProgress" %>
 2 <script type="text/javascript">if (!window.jQuery) alert("使用AjaxProgress必須引用jquery!");</script>
 3 <script type="text/javascript">
 4     $(document).ready(function () {
 5         <% if (this.guid != null && this.guid != "")
 6            {%>
 7                <%="AjaxGetPercent_"+this.ClientID+"('"+this.guid+"');"%>
 8            <%}%>
 9     });
10     function AjaxMethod_<%=this.ClientID%>(clientid) {
11         $.ajax({
12             url:location.href,
13             //content: "Action=ProgressMethod&ClientID=" + clientid + "&ts=" + (new Date).getTime(),
14             data: { Action: "ProgressMethod", ClientID: '<%=this.ClientID%>', ts: (new Date).getTime() },
15             success: function (data) {
16                 if (!(data == undefined || data == null || data == "")) {
17                     if (data != "-1") {
18                         AjaxGetPercent_<%=this.ClientID%>(data);
19                     } else {
20                         alert("此處理過程暫時無法連接,請稍后再試");
21                     }
22                 } else {
23                     alert("訪問的處理不存在,請刷新后重試["+data+"]");
24                 }
25             }
26         });
27     }
28     function AjaxGetPercent_<%=this.ClientID%>(guid) {
29         $.ajax({
30             url: location.href,
31             //content: "Action=GetPercentMethod&ClientID=" + clientid + "&guid=" + guid + "&ts=" + (new Date).getTime(),
32             data: { Action: "GetPercentMethod", ClientID: '<%=this.ClientID%>',guid:guid, ts: (new Date).getTime() },
33             success: function (data) {
34                 if (data != "-1") {
35                     if (data < 100) {
36                         $("#progress_ensleep_pb_<%=this.ClientID%>").css("display", "block");
37                         $("#progress_ensleep_text_<%=this.ClientID%>").html(data);
38                         $("#progress_ensleep_pgress_<%=this.ClientID%>").css("width", data + "%");
39                         setTimeout(AjaxGetPercent_<%=this.ClientID%>(guid), 50);
40                     } else {
41                         $("#progress_ensleep_pgress_<%=this.ClientID%>").css("width", "100%");
42                         if ("<%=this.CloseWhenEnd.Trim()%>" == "true") {
43                             $("#progress_ensleep_pb_<%=this.ClientID%>").css("display", "none");
44                         }
45                         $("#progress_ensleep_text_<%=this.ClientID%>").html(100);
46                         <%=this.JsSuccessCallBack%>
47                     }
48                 } else {
49                     alert("操作過程中出現異常,請重試");
50                 }
51             }
52         });
53     }
54 </script>
55 <style type="text/css">
56         .progressContentensleep{
57             background-color:blue;
58             height:30px;
59             float:left;
60             width:300px;
61         }
62         .progressInnerensleep {
63             background-color:green;
64             height:30px;
65         }
66         .progressSpanensleep{
67             color:white;
68             height:30px;
69             line-height:30px;
70             margin-top:-30px;
71         }
72     </style>
73 <div>
74     <div style="display:none">
75     </div>
76     <div id='progress_ensleep_pb_<%=this.ClientID.Trim() %>' class='<%=this.OuterCssClass %>' style='display: none'>
77         <div id='progress_ensleep_pgress_<%=this.ClientID.Trim() %>' class='<%=this.InnerCssClass %>' style="width:0%">
78         </div>
79          <div id='progress_ensleep_span_<%=this.ClientID.Trim() %>' class='<%=this.TextCss %>'><%=this.WarmText.Substring(0,this.WarmText.IndexOf('{')) %><span id='progress_ensleep_text_<%=this.ClientID.Trim() %>'"></span><%=this.WarmText.Substring(this.WarmText.IndexOf('}')+1,this.WarmText.Length-this.WarmText.IndexOf('}')-1) %></div>
80     </div>
81 </div>

控件的cs代碼:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Linq;
  5 using System.Threading;
  6 using System.Web;
  7 using System.Web.UI;
  8 using System.Web.UI.WebControls;
  9 
 10 namespace ESLib.Controls
 11 {
 12     [ToolboxData("<{0}:AjaxProgress runat=server></{0}:AjaxProgress>")]
 13     [ToolboxItem(true)]
 14     public partial class AjaxProgress : System.Web.UI.UserControl
 15     {
 16         [Description("完成提示,如‘完成{0}%’")]
 17         public string WarmText
 18         {
 19             get { return ViewState[this.ClientID.Trim()+"hfWarmText"].ToString() == "" ? "目前已完成{0}%" : ViewState[this.ClientID.Trim()+"hfWarmText"].ToString(); }
 20             set {
 21                 ViewState[this.ClientID.Trim()+"hfWarmText"] = value;
 22             }
 23 
 24         }
 25         [Description("進度條內芯樣式類")]
 26         public string InnerCssClass
 27         {
 28             get { return ViewState[this.ClientID.Trim()+"hfinnerCss"].ToString() == "" ? "progressInnerensleep" : ViewState[this.ClientID.Trim()+"hfinnerCss"].ToString(); }
 29             set {
 30                 ViewState[this.ClientID.Trim()+"hfinnerCss"] = value;
 31             }
 32         }
 33 
 34         public string CloseWhenEnd
 35         {
 36             get { return ViewState[this.ClientID.Trim()+"hfCloseWhenEnd"].ToString() == "" ? "true" : ViewState[this.ClientID.Trim()+"hfCloseWhenEnd"].ToString() == "true" ? "true" : "false"; }
 37             set { 
 38                 ViewState[this.ClientID.Trim()+"hfCloseWhenEnd"]= value;
 39             }
 40         }
 41         [Description("進度條容器樣式類")]
 42         public string OuterCssClass
 43         {
 44             get { return ViewState[this.ClientID.Trim()+"hfouterCss"].ToString() == "" ? "progressContentensleep" : ViewState[this.ClientID.Trim()+"hfouterCss"].ToString(); }
 45             set
 46             {
 47                 ViewState[this.ClientID.Trim()+"hfouterCss"] = value;
 48             }
 49         }
 50 
 51         [Description("進度條文字樣式類")]
 52         public string TextCss
 53         {
 54             get { return ViewState[this.ClientID.Trim()+"hftextCss"].ToString() == "" ? "progressSpanensleep" : ViewState[this.ClientID.Trim()+"hftextCss"].ToString(); }
 55             set
 56             {
 57                 ViewState[this.ClientID.Trim()+"hftextCss"] = value;
 58             }
 59         }
 60         [Description("達到100%后要執行的js方法,如:js()")]
 61         public string JsSuccessCallBack
 62         {
 63             get { return ViewState[this.ClientID.Trim() + "hfJsSuccessCallBack"].ToString(); }
 64             set {
 65                 ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"] = value;
 66             }
 67         }
 68         [Description("進度條唯一標志符")]
 69         public string guid
 70         {
 71             get {
 72                 try
 73                 {
 74                     return HttpContext.Current.Request.Cookies[this.ClientID.Trim() + "guid"] == null ? "" : HttpContext.Current.Request.Cookies[this.ClientID.Trim() + "guid"].Value.ToString();
 75                 }
 76                 catch (Exception ex)
 77                 {
 78                     return "";
 79                 }
 80             }
 81             set
 82             {
 83                 HttpCookie c = new HttpCookie(this.ClientID.Trim() + "guid");
 84                 c.Value = value;
 85                 HttpContext.Current.Response.Cookies.Add(c);
 86             }
 87         }
 88 
 89         public delegate void DoMethodDelegate(object guid);
 90         public DoMethodDelegate DoMethod;
 91 
 92         public void End(object guid)
 93         {
 94             Application.Remove(guid as String);
 95         }
 96         protected void Page_Load(object sender, EventArgs e)
 97         {
 98             if (!IsPostBack)
 99             {
100                 ViewState[this.ClientID.Trim()+"hfWarmText"] = ViewState[this.ClientID.Trim()+"hfWarmText"] == null ? "" : ViewState[this.ClientID.Trim()+"hfWarmText"];
101                 ViewState[this.ClientID.Trim()+"hfinnerCss"] = ViewState[this.ClientID.Trim()+"hfinnerCss"] == null ? "" : ViewState[this.ClientID.Trim()+"hfinnerCss"];
102                 ViewState[this.ClientID.Trim()+"hfouterCss"] = ViewState[this.ClientID.Trim()+"hfouterCss"] == null ? "" : ViewState[this.ClientID.Trim()+"hfouterCss"];
103                 ViewState[this.ClientID.Trim()+"hftextCss"] = ViewState[this.ClientID.Trim()+"hftextCss"] == null ? "" : ViewState[this.ClientID.Trim()+"hftextCss"];
104                 ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"] = ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"] == null ? "" : ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"];
105                 ViewState[this.ClientID.Trim() + "hfCloseWhenEnd"] = ViewState[this.ClientID.Trim() + "hfCloseWhenEnd"] == null ? "" : ViewState[this.ClientID.Trim() + "hfCloseWhenEnd"];
106             }
107             else
108             {
109             }
110             if (HttpContext.Current.Request.QueryString["Action"] != null)
111             {
112                 HttpContext.Current.Response.Clear();
113                 if (HttpContext.Current.Request.QueryString["ClientID"] != null && (HttpContext.Current.Request.QueryString["ClientID"].ToString().Trim() == this.ClientID.Trim()))
114                 {
115                     HttpContext.Current.Response.ContentType = "text/plain";
116                     if (HttpContext.Current.Request.QueryString["Action"] != null)
117                     {
118                         string command = HttpContext.Current.Request.QueryString["Action"].ToString();
119                         System.Reflection.MethodInfo method = this.GetType().GetMethod(command);
120                         if (method != null)
121                         {
122                             method.Invoke(this,null);
123                         }
124                     }
125                 }
126             }
127         }
128 
129         public void ProgressMethod()
130         {
131             string guid = HttpContext.Current.Session.SessionID.ToString().Trim() + DateTime.Now.ToString("HHmmssffffff").Trim();
132             //Thread th = new Thread(DoMethod(guid));
133             try
134             {
135                 ThreadPool.QueueUserWorkItem(new WaitCallback(this.DoMethod), guid);//未找到處理程序
136                 Application.Add(guid, 0);
137                this.guid= guid;
138                 HttpContext.Current.Response.Write(guid);
139             }
140             catch (Exception ex)
141             {
142                 HttpContext.Current.Response.Write("-1");
143 
144             }
145             HttpContext.Current.Response.End();
146         }
147         public void GetPercentMethod()
148         {
149             try
150             {
151                 string guid = HttpContext.Current.Request.QueryString["guid"].ToString();
152                 if (Application[guid] != null)
153                 {
154                     HttpContext.Current.Response.Write(Application[guid].ToString());
155                 }
156                 else
157                 {
158                     HttpCookie c = new HttpCookie(this.ClientID.Trim() + "guid");
159                     c.Expires = DateTime.Now.AddDays(-1);
160                     HttpContext.Current.Response.Cookies.Add(c);
161                     HttpContext.Current.Response.Write("100");
162                 }
163             }
164             catch (Exception ex)
165             {
166                 HttpContext.Current.Response.Write("-1");
167             }
168             HttpContext.Current.Response.End();
169         }
170     }
171 }

由於時間問題,就不多說了,代碼全在這里了,我是覺得,我被這個東西擋了好多次了,然后網上一直沒有類似的控件,讓人着實難受。

寫好的文件在這里,http://url.cn/OK26ls (密碼:SEvf)

下面是用法,測試的童鞋可以看一下:

使用說明:

 

            <UC:AjaxProgress runat = "server" ID = "AjaxProgress2" JsSuccessCallBack = "sucessajax3()" InnerCssClass = "progressInner" OuterCssClass = "progressContent" WarmText = "{0}%" TextCss = "progressSpan" CloseWhenEnd="false" />

說明:

     屬性

  1. JsSuccessCallBack       進度達到100%后執行的js函數
  2. InnerCssClass           進度條內條樣式類
  3. OuterCssClass           進度條窗口樣式類
  4. WarmText                提示文字,{0}為百分數(無百分號)的通配符
  5. TextCss             顯示進度文件樣式類
  6. CloseWhenEnd            進度完成后是否關閉進度條,false不關閉,其它情況默認為關閉

 

事件:

 

AjaxProgress2.DoMethod = DoMethod2;
public void DoMethod2(object guid)

       {

            for (int i = 0; i < 101; i++)

            {

                Thread.Sleep(500);

                Application[guid as String] = i;

            }

}

在aspx.cs中,必須要聲明一個方法,並且將此方法賦值給控件的委托DoMethod

 

 

觸發進度條函數為:   

            AjaxMethod _【AjaxProgress的ClientID】()

    解析:

AjaxProgress的ClientID,即在瀏覽器中的id,由於沒有呈現器,所以,此處與ID相同,即樣例中的AjaxProgress2。

例如:

     <asp:Button runat = "server" ID="Button2" Text = "執行3" OnClientClick = "AjaxMethod_AjaxProgress2();return false;" />

 

進度條后台執行事件:

    數據委托定義:public delegate void DoMethodDelegate(object guid);

    所以,可以定義成如下:


public void DoMethod2(object guid)
{
for (int i = 0; i < 101; i++)
{
Thread.Sleep(500);
Application[guid as String] = i;
}
AjaxProgress2.End(guid);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM