• 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="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);
    }

  • 相关阅读:
    winform中的 listview的使用的讲解。
    快乐的一天从AC开始 | 20210716 | P1345
    快乐的一天从AC开始 | 20210715 | P4643
    快乐的一天从AC开始 | 20210714 | P3594
    快乐的一天从AC开始 | 20210713 | P3557
    快乐的一天从AC开始 | 20210712 | P2251
    P7294-[USACO21JAN]Minimum Cost Paths P【单调栈】
    AT4353-[ARC101D]Robots and Exits【LIS】
    2021牛客暑期多校训练营9C-Cells【LGV引理,范德蒙德行列式】
    Loj#2880-「JOISC 2014 Day3」稻草人【CDQ分治,单调栈,二分】
  • 原文地址:https://www.cnblogs.com/ensleep/p/3459919.html
Copyright © 2020-2023  润新知