您的位置:首页 > 其它

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

2009-09-12 18:09 351 查看
由于 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保存程序执行进度的方法也是不完全正确的。



那么终极的方法是什么呢?对,将程序执行进度保存在第三方的存储介质上,如数据库,文件系统等等都是可以的。这个方法代码我就不写了,就是增加访问数据库的部分即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐