asp.net 后台多线程异步处理时的 进度条实现一(Ajax+Ashx实现以及封装成控件的实现)
(更新:有的同学说源代码不想看,说明也不想看,只想要一个demo,这边提供一下:http://url.cn/LPT50k (密码:TPHU))
工作好长时间了,这期间许多功能也写成了不少的控件来使用,但是,都只是为了代码的结构清析一些而已。而这一次,我决定完成一个我一直在网上寻找却没寻找到的功能。就是,在异步(比如说,后台的数据库备份、后台的文件加解密这类操作)时,前台假死的情况。asp自带了updatePanel,里面可以放一个自带的progress控件,怎么说呢,这就是一个显示而已,而且根本不能动。当你在备份数据的时候,你点备份,然后上面显示“请等待....”,这叫progress,我擦。不过,微软官方给了解释了,说许多人习惯了观察浏览器状态栏上面的进度条,我想说那个条子好假,不信你试试。
好了,下面来说一下我做的这个东西,当然,我先用ajax+ashx的方式想办法让它实现,下面是我设计前期自己画的一张图:
这张图上面有错误,但是,作为我的第一个想法,我觉得它功不可没,后面的图都是在它的基础上修改而来了,它也代表了我的初始想法,以及相关的知识残缺。
图中已经可以把我大部分的想法表达出来了,当然会有人说,通常ajax轮询进度都是这么实现的。但是,却没有人将它封装成服务器控件。
先说图中明显的错误:
1、异步操作的时候,一旦请求终断,线程则无法再访问到session,这是一个致命的异常。因为,我本来打算以session来保存信息,并依靠session的机制来释放我已经保存的内容,可以省下好多流程与精力,但事实证明,我还是太年轻了~~。所以,我改用了application,写就了我的第一个后台ashx文件:
public class AjaxAction : IHttpHandler, IReadOnlySessionState
{ public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
if (context.Request.QueryString["Action"] != null)
{
string command = context.Request.QueryString["Action"].ToString();
System.Reflection.MethodInfo method = this.GetType().GetMethod(command);
if (method != null)
{
method.Invoke(this, new object[] { context });
}
}
} public void ProgressMothed(HttpContext context)
{
string guid = HttpContext.Current.Session.SessionID.ToString().Trim() + DateTime.Now.ToString("HHmmssffffff").Trim();
Thread th = new Thread(
delegate()
{
for (int i = ; i < ; i++)
{
Thread.Sleep();
context.Application[guid] = i;
context.Response.Write(i.ToString());
} }
);
th.IsBackground = true;
try
{
th.Start();
context.Application.Add(guid, );
context.Response.Write(guid);
}
catch (Exception ex)
{
context.Response.Write("-1");
}
}
public void GetPercentMethod(HttpContext context)
{
try
{
string guid = context.Request.QueryString["guid"].ToString();
string per = context.Application[guid].ToString();
context.Response.Write(per);
}
catch (Exception ex)
{
context.Response.Write("-1");
}
}
public bool IsReusable
{
get
{
return false;
}
}
<script type="text/javascript"> function AjaxMethod() {
$.ajax({
url:location.href,
data: { Action: "ProgressMethod", ts: (new Date).getTime() },
success: function (data) {
if (!(data == undefined || data == null || data == "")) {
if (data != "-1") {
AjaxGetPercent(data);
} else {
alert("此处理过程暂时无法连接,请稍后再试");
}
} else {
alert("访问的处理不存在,请刷新后重试["+data+"]");
}
}
});
}
function AjaxGetPercent(guid) {
$.ajax({
url: location.href,
data: { Action: "GetPercentMethod",guid:guid, ts: (new Date).getTime() },
success: function (data) {
if (data != "-1") {
if (data < 100) {
$("#progress_ensleep_pb").css("display", "block");
$("#progress_ensleep_text").html(data);
$("#progress_ensleep_pgress").css("width", data + "%");
setTimeout(AjaxGetPercent(guid), 50);
} else {
$("#progress_ensleep_pgress").css("width", "100%"); $("#progress_ensleep_text").html(100);
}
} else {
alert("操作过程中出现异常,请重试");
}
}
});
}
</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请求。
下面上代码:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="AjaxProgress.ascx.cs" Inherits="ESLib.Controls.AjaxProgress" %>
<script type="text/javascript">if (!window.jQuery) alert("使用AjaxProgress必须引用jquery!");</script>
<script type="text/javascript">
$(document).ready(function () {
<% if (this.guid != null && this.guid != "")
{%>
<%="AjaxGetPercent_"+this.ClientID+"('"+this.guid+"');"%>
<%}%>
});
function AjaxMethod_<%=this.ClientID%>(clientid) {
$.ajax({
url:location.href,
//content: "Action=ProgressMethod&ClientID=" + clientid + "&ts=" + (new Date).getTime(),
data: { Action: "ProgressMethod", ClientID: '<%=this.ClientID%>', ts: (new Date).getTime() },
success: function (data) {
if (!(data == undefined || data == null || data == "")) {
if (data != "-1") {
AjaxGetPercent_<%=this.ClientID%>(data);
} else {
alert("此处理过程暂时无法连接,请稍后再试");
}
} else {
alert("访问的处理不存在,请刷新后重试["+data+"]");
}
}
});
}
function AjaxGetPercent_<%=this.ClientID%>(guid) {
$.ajax({
url: location.href,
//content: "Action=GetPercentMethod&ClientID=" + clientid + "&guid=" + guid + "&ts=" + (new Date).getTime(),
data: { Action: "GetPercentMethod", ClientID: '<%=this.ClientID%>',guid:guid, ts: (new Date).getTime() },
success: function (data) {
if (data != "-1") {
if (data < 100) {
$("#progress_ensleep_pb_<%=this.ClientID%>").css("display", "block");
$("#progress_ensleep_text_<%=this.ClientID%>").html(data);
$("#progress_ensleep_pgress_<%=this.ClientID%>").css("width", data + "%");
setTimeout(AjaxGetPercent_<%=this.ClientID%>(guid), 50);
} else {
$("#progress_ensleep_pgress_<%=this.ClientID%>").css("width", "100%");
if ("<%=this.CloseWhenEnd.Trim()%>" == "true") {
$("#progress_ensleep_pb_<%=this.ClientID%>").css("display", "none");
}
$("#progress_ensleep_text_<%=this.ClientID%>").html(100);
<%=this.JsSuccessCallBack%>
}
} else {
alert("操作过程中出现异常,请重试");
}
}
});
}
</script>
<style type="text/css">
.progressContentensleep{
background-color:blue;
height:30px;
float:left;
width:300px;
}
.progressInnerensleep {
background-color:green;
height:30px;
}
.progressSpanensleep{
color:white;
height:30px;
line-height:30px;
margin-top:-30px;
}
</style>
<div>
<div style="display:none">
</div>
<div id='progress_ensleep_pb_<%=this.ClientID.Trim() %>' class='<%=this.OuterCssClass %>' style='display: none'>
<div id='progress_ensleep_pgress_<%=this.ClientID.Trim() %>' class='<%=this.InnerCssClass %>' style="width:0%">
</div>
<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>
</div>
</div>
控件的cs代码:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls; namespace ESLib.Controls
{
[ToolboxData("<{0}:AjaxProgress runat=server></{0}:AjaxProgress>")]
[ToolboxItem(true)]
public partial class AjaxProgress : System.Web.UI.UserControl
{
[Description("完成提示,如‘完成{0}%’")]
public string WarmText
{
get { return ViewState[this.ClientID.Trim()+"hfWarmText"].ToString() == "" ? "目前已完成{0}%" : ViewState[this.ClientID.Trim()+"hfWarmText"].ToString(); }
set {
ViewState[this.ClientID.Trim()+"hfWarmText"] = value;
} }
[Description("进度条内芯样式类")]
public string InnerCssClass
{
get { return ViewState[this.ClientID.Trim()+"hfinnerCss"].ToString() == "" ? "progressInnerensleep" : ViewState[this.ClientID.Trim()+"hfinnerCss"].ToString(); }
set {
ViewState[this.ClientID.Trim()+"hfinnerCss"] = value;
}
} public string CloseWhenEnd
{
get { return ViewState[this.ClientID.Trim()+"hfCloseWhenEnd"].ToString() == "" ? "true" : ViewState[this.ClientID.Trim()+"hfCloseWhenEnd"].ToString() == "true" ? "true" : "false"; }
set {
ViewState[this.ClientID.Trim()+"hfCloseWhenEnd"]= value;
}
}
[Description("进度条容器样式类")]
public string OuterCssClass
{
get { return ViewState[this.ClientID.Trim()+"hfouterCss"].ToString() == "" ? "progressContentensleep" : ViewState[this.ClientID.Trim()+"hfouterCss"].ToString(); }
set
{
ViewState[this.ClientID.Trim()+"hfouterCss"] = value;
}
} [Description("进度条文字样式类")]
public string TextCss
{
get { return ViewState[this.ClientID.Trim()+"hftextCss"].ToString() == "" ? "progressSpanensleep" : ViewState[this.ClientID.Trim()+"hftextCss"].ToString(); }
set
{
ViewState[this.ClientID.Trim()+"hftextCss"] = value;
}
}
[Description("达到100%后要执行的js方法,如:js()")]
public string JsSuccessCallBack
{
get { return ViewState[this.ClientID.Trim() + "hfJsSuccessCallBack"].ToString(); }
set {
ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"] = value;
}
}
[Description("进度条唯一标志符")]
public string guid
{
get {
try
{
return HttpContext.Current.Request.Cookies[this.ClientID.Trim() + "guid"] == null ? "" : HttpContext.Current.Request.Cookies[this.ClientID.Trim() + "guid"].Value.ToString();
}
catch (Exception ex)
{
return "";
}
}
set
{
HttpCookie c = new HttpCookie(this.ClientID.Trim() + "guid");
c.Value = value;
HttpContext.Current.Response.Cookies.Add(c);
}
} public delegate void DoMethodDelegate(object guid);
public DoMethodDelegate DoMethod; public void End(object guid)
{
Application.Remove(guid as String);
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ViewState[this.ClientID.Trim()+"hfWarmText"] = ViewState[this.ClientID.Trim()+"hfWarmText"] == null ? "" : ViewState[this.ClientID.Trim()+"hfWarmText"];
ViewState[this.ClientID.Trim()+"hfinnerCss"] = ViewState[this.ClientID.Trim()+"hfinnerCss"] == null ? "" : ViewState[this.ClientID.Trim()+"hfinnerCss"];
ViewState[this.ClientID.Trim()+"hfouterCss"] = ViewState[this.ClientID.Trim()+"hfouterCss"] == null ? "" : ViewState[this.ClientID.Trim()+"hfouterCss"];
ViewState[this.ClientID.Trim()+"hftextCss"] = ViewState[this.ClientID.Trim()+"hftextCss"] == null ? "" : ViewState[this.ClientID.Trim()+"hftextCss"];
ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"] = ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"] == null ? "" : ViewState[this.ClientID.Trim()+"hfJsSuccessCallBack"];
ViewState[this.ClientID.Trim() + "hfCloseWhenEnd"] = ViewState[this.ClientID.Trim() + "hfCloseWhenEnd"] == null ? "" : ViewState[this.ClientID.Trim() + "hfCloseWhenEnd"];
}
else
{
}
if (HttpContext.Current.Request.QueryString["Action"] != null)
{
HttpContext.Current.Response.Clear();
if (HttpContext.Current.Request.QueryString["ClientID"] != null && (HttpContext.Current.Request.QueryString["ClientID"].ToString().Trim() == this.ClientID.Trim()))
{
HttpContext.Current.Response.ContentType = "text/plain";
if (HttpContext.Current.Request.QueryString["Action"] != null)
{
string command = HttpContext.Current.Request.QueryString["Action"].ToString();
System.Reflection.MethodInfo method = this.GetType().GetMethod(command);
if (method != null)
{
method.Invoke(this,null);
}
}
}
}
} public void ProgressMethod()
{
string guid = HttpContext.Current.Session.SessionID.ToString().Trim() + DateTime.Now.ToString("HHmmssffffff").Trim();
//Thread th = new Thread(DoMethod(guid));
try
{
ThreadPool.QueueUserWorkItem(new WaitCallback(this.DoMethod), guid);//未找到处理程序
Application.Add(guid, );
this.guid= guid;
HttpContext.Current.Response.Write(guid);
}
catch (Exception ex)
{
HttpContext.Current.Response.Write("-1"); }
HttpContext.Current.Response.End();
}
public void GetPercentMethod()
{
try
{
string guid = HttpContext.Current.Request.QueryString["guid"].ToString();
if (Application[guid] != null)
{
HttpContext.Current.Response.Write(Application[guid].ToString());
}
else
{
HttpCookie c = new HttpCookie(this.ClientID.Trim() + "guid");
c.Expires = DateTime.Now.AddDays(-);
HttpContext.Current.Response.Cookies.Add(c);
HttpContext.Current.Response.Write("");
}
}
catch (Exception ex)
{
HttpContext.Current.Response.Write("-1");
}
HttpContext.Current.Response.End();
}
}
}
由于时间问题,就不多说了,代码全在这里了,我是觉得,我被这个东西挡了好多次了,然后网上一直没有类似的控件,让人着实难受。
写好的文件在这里,http://url.cn/OK26ls (密码:SEvf)
下面是用法,测试的童鞋可以看一下:
使用说明:
<UC:AjaxProgress runat = "server" ID = "AjaxProgress2" JsSuccessCallBack = "sucessajax3()" InnerCssClass = "progressInner" OuterCssClass = "progressContent" WarmText = "{0}%" TextCss = "progressSpan" CloseWhenEnd="false" />
说明:
属性
- JsSuccessCallBack 进度达到100%后执行的js函数
- InnerCssClass 进度条内条样式类
- OuterCssClass 进度条窗口样式类
- WarmText 提示文字,{0}为百分数(无百分号)的通配符
- TextCss 显示进度文件样式类
- 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);
}
asp.net 后台多线程异步处理时的 进度条实现一(Ajax+Ashx实现以及封装成控件的实现)的更多相关文章
- Java实现在复制文件时使用进度条
在对大文件操作时,可能会需要些时间,此时为用户提供进度条提示是非常常见的一项功能,这样用户就可以了解操作文件需要的时间信息.本实例为大家介绍了在复制大的文件时使用的进度条提示,需要注意的是,只有在读取 ...
- android AsyncTask异步下载并更新进度条
AsyncTask异步下载并更新进度条 //如果不是很明白请看上篇文章的异步下载 AsyncTask<String, Integer, String> 第一个参数:String 传入 ...
- Android开发之多线程下载、断点续传、进度条和文本显示
代码实现了在Android环境下的多线程下载.断点续传.进度条显示和文本显示百分数: import java.io.BufferedReader; import java.io.File; impor ...
- android 网络异步加载数据进度条
ProgressDialog progressDialog = null; public static final int MESSAGETYPE = 0; private void execute( ...
- js 文件异步上传 显示进度条 显示上传速度 预览文件
通常文件异步提交有几个关键 1.支持拖拽放入文件.2.限制文件格式.3.预览图片文件.4.上传进度,速度等,上传途中取消上传.5.数据与文件同时上传 现在开始笔记: 需要一个最基础的元素<inp ...
- C#调用耗时函数时显示进度条浅探
最近在做一个VSS日志分析工具,使用C#进行开发,在完成了所有功能后,发现,从服务器下载VSS日志非常耗时,因为此,导致工具使用体验不好,所以,准备增加一个进度条.鉴于C#不经常使用,一下子搞个进度条 ...
- WPF BackGroundWord 异步加载更新进度条示例
<Window x:Class="AsynchronousLoading.MainWindow" xmlns="http://schemas.microsoft.c ...
- Asp.net mvc 大文件上传 断点续传 进度条
概述 项目中需要一个上传200M-500M的文件大小的功能,需要断点续传.上传性能稳定.突破asp.net上传限制.一开始看到51CTO上的这篇文章,此方法确实很不错,能够稳定的上传大文件,http: ...
- NGUI的异步场景加载进度条
1.直接创建三个场景,其中第二个场景是用来显示进度条加载的界面,进度条用UISlider,不会的看我前面的博文就可以了. 2.这里提供两种方法,建议使用第一种,加载比较平缓 方法一: using Sy ...
随机推荐
- MySQL冗余数据的三种方案
一,为什么要冗余数据 互联网数据量很大的业务场景,往往数据库需要进行水平切分来降低单库数据量. 水平切分会有一个patition key,通过patition key的查询能够直接定位到库,但是非pa ...
- git的使用学习(七)githup和码云的使用
1.使用GitHub 我们一直用GitHub作为免费的远程仓库,如果是个人的开源项目,放到GitHub上是完全没有问题的.其实GitHub还是一个开源协作社区,通过GitHub,既可以让别人参与你的开 ...
- 根据CPU核数合理设置线程池大小
一般来说池中总线程数是核心池线程数量两倍,只要确保当核心池有线程停止时,核心池外能有线程进入核心池即可. 我们所需要关心的主要是核心池线程的数量该如何设置. 自定义线程池代码 package com. ...
- JAVA基础之List接口
个人理解: list接口是Collection接口的子类,其继承了Collection接口的所有方法,但也有其独有的方法,不过在迭代的时候不要进行任何操作.牢记数据存储的四种结构:堆栈.队列.数组.链 ...
- 百度前端开发规范 by fex-team
github:https://github.com/fex-team/styleguide 离线版本: 链接:http://pan.baidu.com/s/1gfr857l 密码:cvk3 注:只支持 ...
- rapidxml读取包含中文路径的xml解析错误的解决方法
from http://blog.csdn.net/qinwei4072880/article/details/38865179 1.rapidxml不支持中文路径. 2.rapidxml不支持Uni ...
- novell.directory.ldap获取邮箱活动目录
在windows系统上可以使用下列方法来查找所有的员工邮箱和员工组: StringDictionary ReturnArray = new StringDictionary(); Dictionary ...
- python 类和__class__理解
__class__可理解为对象所属的父类 class A: def __init__(self,url): self.url = url def out(self): return self.url ...
- 区间最小值(2) (线段树 更新区间)2015年 JXNU_ACS 算法组暑假第一次周赛
区间最小值(2) Time Limit : 3000/1000ms (Java/Other) Memory Limit : 65535/32768K (Java/Other) Total Subm ...
- Vbs脚本经典教材
转载:http://www.cnblogs.com/BeyondTechnology/archive/2011/01/10/1932440.html Vbs脚本经典教材(最全的资料还是MSDN) —为 ...