现在的位置: 首页 > 综合 > 正文

在客户端显示服务器端任务处理进度条的探讨

2013年10月05日 ⁄ 综合 ⁄ 共 7433字 ⁄ 字号 评论关闭

 
由于 HTTP 协议本身的无状态性,B/S结构的程序无法像C/S程序那样,实时显示程序处理的进度。搜索一下网上,一般都是采用静态变量保存程序执行进度的方法实现,但是,这种方法是完全错误的,在并发的情况下,多个用户访问一个程序,会造成混乱。

 

下面就是采用静态变量的方法实现的:

 

 <%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1
-

transitional.dtd">
<script runat="server">
  private static int Processbar = 0;
  private static int TotalCount = 100; //设置初始值,防止出现被0除。
  protected void ProcessTask()
  {
    //通过计算,得出TotalCount的值,比如查询数据库等
    TotalCount = 150;
    while (Processbar < TotalCount)
    {
      Processbar += 5;
      System.Threading.Thread.Sleep(1000);
    }
  }

  protected void Page_Load(object sender, EventArgs e)
  {
    if (Request.QueryString["step"] != null && Request.QueryString["step"].Equals(String.Empty) == false)
    {
      if (Request.QueryString["step"].Equals("1"))
      {
        Processbar = 0;
        System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(ProcessTask));
        thread.Start();
        Response.ClearContent();
        Response.Write(0);
        Response.End();
      }
      else
      {
        Response.ClearContent();
        if (Processbar < TotalCount)
        {
          Response.Write(Processbar * 100 / TotalCount);
        }
        else
        {
          Response.Write("ok");
        }
        Response.End();
      }
    }
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml
">
<head runat="server">
  <title>在客户端显示服务器端任务处理进度条的探讨</title>
  <script type="text/javascript">
    var http = null;
    var count = 1;
    var timer = null;
    function createXMLHTTP() {
      return window.XMLHttpRequest ? new window.XMLHttpRequest() : new window.ActiveXObject("MSXML2.XMLHTTP");
    }
    function showProcess() {
      http = createXMLHTTP()
      http.open("GET", "<%=Request.Url.ToString() %>?step=" + (count++) + "&" + Date.parse(new Date()), true);
      http.onreadystatechange = function() {
        if (http.readyState == 4 && http.status == 200)
          if ("ok" == http.responseText) {
          document.getElementById("process").innerHTML = "完成";
          window.clearInterval(timer);
        }
        else {
          document.getElementById("process").innerHTML = http.responseText + "%";
        }
      }
      http.send(null);
    }

    function startTask() {
      count = 1;
      document.getElementById("process").innerHTML =  "0%";
      timer = window.setInterval("showProcess()", 1000);
      return false;
    }
  </script>

</head>
<body>
  <form id="form1" runat="server">
  <input type="button" value="开始处理长时间操作" onclick="return startTask();" />
  <div id="process"></div>
  </form>
</body>
</html>

 

 

 

这种方法,在一个用户访问的情况下是没有问题的,但多个用户访问时就会造成混乱。

 

下面这这种方法,是常用的方法,一般情况下可以满足需求:

 <%@ Page Language="C#" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1
-

transitional.dtd">

<script runat="server">
  /// <summary>
  /// 设置全局变量,以便不同的方法是用
  /// </summary>
  private int Processbar = 0; //设置初始的状态,也可以代表一系列步骤中的每个步骤。
  private int TotalCount = 100; //设置初始值,防止出现被0除。
  private String key;
  protected void ProcessTask()
  {
    while (Processbar < TotalCount)
    {
      Processbar = this.GetProcessbar() + 5; //这里只是模拟一下,每次加 5
      System.Threading.Thread.Sleep(1000); //这里只是模拟一个长时间的执行过程。
      SaveData();
    }
  }

  protected void Page_Load(object sender, EventArgs e)
  {
    key = Request.QueryString["guid"];  //多个并发请求时,用来区分客户端的请求。
    if (String.IsNullOrEmpty(key)) key = Guid.NewGuid().ToString();
    Processbar = this.GetProcessbar();
    TotalCount = this.GetTotalCount();

    //以下判断原来判断请求的不同过程,是第一次请求,还是更新进度条的请求,实现方法也可以划分为多个程序来实现。
    if (Request.QueryString["step"] != null && Request.QueryString["step"].Equals(String.Empty) == false)
    {
      if (Request.QueryString["step"].Equals("1"))
      {
        // 开始执行任务的请求,启动长时间的任务处理。
        Processbar = 0;
        //通过计算,得出TotalCount的值,比如查询数据库等,也可以是一个任务的多个步骤的总和。
        TotalCount = 200; //假如完成一个任务需要200个步骤
        SaveData();
        System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(ProcessTask));
        thread.Start();
        Response.ClearContent();
        Response.Write(0);
        Response.End();
      }
      else
      {
        Response.ClearContent();
        if (Processbar < TotalCount)
        {
          // 输出处理的过程
          Response.Write(Processbar * 100 / TotalCount);
        }
        else
        {
          // 所有的任务都完成了,输出结束信息,终止前端的请求。
          Response.Write("ok");
          Cache.Remove(key);
        }
        Response.End();
      }
    }
    else
    {
      G.Text = key;
      if (System.IO.File.Exists(Server.MapPath(key + ".txt")))
      {
        System.IO.File.Delete(Server.MapPath(key + ".txt"));
      }
    }
  }

  /// <summary>
  /// 得到执行过程的阶段
  /// </summary>
  /// <returns></returns>
  private int GetProcessbar()
  {
    String data = Convert.ToString(Cache.Get(key));
    if (String.IsNullOrEmpty(data))
      return 0;
    else
    {
      return Convert.ToInt32(data.Split(',')[0]);
    }
  }

  /// <summary>
  /// 得到全部的过程数
  /// </summary>
  /// <returns></returns>
  private int GetTotalCount()
  {
    String data = Convert.ToString(Cache.Get(key));
    if (String.IsNullOrEmpty(data))
      return 0;
    else
    {
      return Convert.ToInt32(data.Split(',')[1]);
    }
  }

  /// <summary>
  /// 将过程保存。
  /// </summary>
  private void SaveData()
  {
    WriteLog();
    Cache.Insert(key, Processbar.ToString() + "," + TotalCount.ToString());
  }

  private void WriteLog()
  {
    System.IO.StreamWriter sw = new System.IO.StreamWriter(Server.MapPath(key + ".txt"), true);
    sw.WriteLine("Processbar = " + Processbar + " TotalCount = " + TotalCount + " " + System.DateTime.Now.ToString

());
    sw.Close();
  }
</script>

<html xmlns="http://www.w3.org/1999/xhtml
">
<head id="Head1" runat="server">
  <title>在客户端显示服务器端任务处理进度条的探讨</title>

  <script type="text/javascript">
    var http = null;
    var count = 1;
    var timer = null;
    var guid = "<asp:Literal id='G' runat='server'/>";
    function createXMLHTTP() {
      return window.XMLHttpRequest ? new window.XMLHttpRequest() : new window.ActiveXObject("MSXML2.XMLHTTP");
    }
    function showProcess() {
      http = createXMLHTTP();
      http.open("GET", "<%=Request.Url.ToString() %>?step=" + (count++) + "&guid=" + guid + "&" + Date.parse(new

Date()), true);
      http.setRequestHeader("Connection", "close");
      http.onreadystatechange = function() {
        if (http.readyState == 4 && http.status == 200)
          if ("ok" == http.responseText) {
          document.getElementById("process").innerHTML = document.getElementById("processbar1").innerHTML = "完成";
          document.getElementById("processbar2").style.width = "100%";
          window.clearInterval(timer);
        }
        else {
          document.getElementById("process").innerHTML = document.getElementById("processbar1").innerHTML =

http.responseText + "%";
          document.getElementById("processbar2").style.width = http.responseText + "%";
        }
      }
      http.send(null);
    }

    function startTask() {
      count = 1;
      document.getElementById("process").innerHTML = document.getElementById("processbar1").innerHTML = "0%";
      document.getElementById("processbar2").style.width = "0%";
      timer = window.setInterval("showProcess()", 1000);
      return false;
    }
  </script>

</head>
<body>
  <form id="form1" runat="server">
  <input type="button" value="启动处理长时间操作" onclick="return startTask();" /> 
  <div style="border: 1px solid blue; width: 600px; position: relative;margin:10px 0;">
    <div style="background: #f00; width: 0; height: 20px;" id="processbar2"></div>
    <div style="position: absolute; text-align: center; top: 0; width: 100%" id="processbar1"></div>
  </div>
  <div id="process"></div>
  </form>
</body>
</html>

 

代码执行效果:

 孟宪会

 

但是,这种方法就是万事大吉了吗?完全错误,这种方法仍然存在显示不准确的现象,造成显示不准确的原因就是 Cache 的使用,IIS 6之后,增加了应用程序池的功能,这个功能可以大大提高程序的性能,减少程序本身的错误导致的整个网站的崩溃。但是,如果应用程序池的“性能”-“Web 园”数目设置大于1的情况下,HttpApplicationState(Application)、Cache、HttpSessionState(Session)这些变量都是都是无法使用了,这是因为:每个Web 园会启动一个w3wp.exe工作进程,每个工作进程之间是相互独立的,以上这些变量也就是不是共享的了,所以,使用Cache保存程序执行进度的方法也是不完全正确的。

 

那么终极的方法是什么呢?对,将程序执行进度保存在第三方的存储介质上,如数据库,文件系统等等都是可以的。这个方法代码我就不写了,就是增加访问数据库的部分即可。

抱歉!评论已关闭.