首页 技术 正文
技术 2022年11月19日
0 收藏 434 点赞 5,029 浏览 7425 个字

http://www.cnblogs.com/wfyfngu/p/4866434.html

  在传统桌面项目中,进度条随处可见,但作为一个很好的用户体验,却没有在如今主流的B/S程序中得到传承,不能不说是个遗憾。这个遗憾并非WEB程序不支持进度条,很大的原因应该是我们由于种种麻烦懒的去实现。前段时间由于新项目等待客户验收,有点闲暇时间,于是突发奇想决定给新系统的所有导出功能添加进度提示,替换现正使用的只简单显示一个Loading图片作为提示的方法,于是有了本文。

  实现的思路很简单:服务器端收到客户端一个需要较长时间执行的任务 -> 服务器端开始执行这个任务并创建一个 Progress 状态,保存在 Cache 中,在任务执行过程中,不断更新进度 -> 同时,客户端新取一个线程(异步),每隔0.5秒(经过测试,0.5秒是一个比较好的时间,既不容易造成客户端网络拥堵,也能带来相当好的用户体验)访问一次服务器以获得该任务的进度并呈现给终端用户。

  下面是本人的实现方法,它能支持所有需要精确计算进度的任务,觉得有一定的参考性,前后台代码分别采用 Javascript 和 C# 实现,分享给大家。

  服务器端我们需要做 2 件事情:进度管理类和一个供客户端查询状态的页面(采用 Handler实现)。

  首先是进度管理类,主要用于记录任务总数和当前已经完成的数目,同时自行管理缓存状态,以方便客户端随时访问。代码如下:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172 using Cache;using System; namespace RapidWebTemplate.WebForm {     /// <summary>    /// 服务器事件进度服务    /// </summary>    public sealed class ProgressService {         // 缓存保存的时间,可根据自己的项目设置        private const int CACHE_SECONDS = 600;                 // 任务唯一ID        private string _key = null;         private ProgressService() { }         /// <summary>        /// 获取或设置总进度        /// </summary>        public int Total { getset; }        /// <summary>        /// 获取或设置已经完成的进度        /// </summary>        public int Elapsed { getset; }         /// <summary>        /// 获取已经完成的进度百分比        /// </summary>        public byte ElapsedPercent {            get {                if (Finished) { return 100; }                double d = (double)Elapsed / (double)Total * 100d;                var tmp = Convert.ToInt32(Math.Ceiling(d));                if (tmp > 100) { tmp = 100; }                if (tmp < 0) { tmp = 0; }                return Convert.ToByte(tmp);            }        }         /// <summary>        /// 获取一个值,该值指示当前进度是否已经完成        /// </summary>        public bool Finished {            get return Elapsed >= Total; }        }         public void Remove() {            try {                CacheFactory.Remove(_key);            catch { }        }                  /// <summary>        /// 获取一个缓存中的进度对象或创建一个全新的进度对象并添加到缓存中        /// </summary>        /// <param name="key"></param>        /// <returns></returns>        public static ProgressService GetInstance(string key) {            var obj = CacheFactory.GetCache(key) as ProgressService;            if (obj == null) {                obj = new ProgressService();                obj._key = key;                CacheFactory.Add(key, obj, DateTime.Now.AddSeconds(CACHE_SECONDS));            }            return obj;        }     }}

  接下来是查询页面,命名为 Progress.ashx,后台代码如下:

12345678910111213141516171819202122232425262728293031 using RapidWebTemplate.WebForm;using System;using System.Collections.Generic;using System.Linq;using System.Web;using Utility;using Utility.Http; namespace Web.Handlers {     /// <summary>    /// 获取服务端指定任务执行的进度    /// </summary>    public class Progress : IHttpHandler {         public void ProcessRequest(HttpContext context) {            context.Response.ContentType = "text/json";            var key = FormHelper.GetString(context.Request.QueryString["progress_key"]);            var obj = ProgressService.GetInstance(key);            context.Response.Write(obj.ToJSON());            if (obj.Finished) {                obj.Remove();            }        }         public bool IsReusable {            get return false; }        }     }}

  到此,我们已经完成了后台代码的编写,下面将是前台代码的编写,这里以导出 Excel 为例,代码如下:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768 var js={doExport:function(icon){    var key=''+(new Date().getTime())+(Math.floor(Math.random()*10));    var btns=$('#btnExport');    var showProgress=false;    if(btns.size()>0){      //var form=btns.first().parent('form');      //form.attr('target','_blank');      var input=$('#download_key');      if(input.size()>0){        input.val(key);      }else{        btns.first().parents('form').append('<input type="hidden" id="download_key" name="download_key" value="'+key+'"/>');      }      btns.first().trigger('click');      showProgress=true;    }else{      js.info('Not supported.');    }    var me=this;    setTimeout(function(){      $(document.body).hideLoading();      if(showProgress){        me._showProgress(key);      }    },500);  },  _showProgress:function(key){    var id='progress_bar';    var me=this;    if($('#'+id).size()>0){    }else{      $(document.body).append('<div id="'+id+'_dialog"><div id="'+id+'"></div></div>');    }    $('#'+id+'_dialog').dialog({      //title:'\u8bf7\u7a0d\u540e...', // please wait      width:400,      //height:60,      modal:true,      closable:false,      border:false,      noheader:true,      onOpen:function(){        $(this).children().first().progressbar({value:0});        setTimeout(function(){          me._updateProgessState(key,id,me);        },1000);      }    });  },  _progressStateTimer:null,  _updateProgessState:function(key,id,ns){    var url='/Handlers/Progress.ashx?progress_key='+key;    url+='&ran='+(new Date().getTime());    $.get(url,function(res){      //res={"Total":0,"Elapsed":0,"ElapsedPercent":100,"Finished":true}      if(res.Finished){        $('#'+id).progressbar('setValue',100);        setTimeout(function(){ $('#'+id+'_dialog').dialog('destroy');},500); // Wait for 0.5 seconds to close the progress dialog        clearTimeout(ns._progressStateTimer);      }else{        //alert(res.Elapsed);        $('#'+id).progressbar('setValue',res.ElapsedPercent);        ns._progressStateTimer=setTimeout(function(){ns._updateProgessState(key,id,ns);},500);      }    });  },};

  所有必要的代码已经编写完成,下面是服务器端对进度服务的使用,还是以导出 Excel 为例,需要在原有的代码基础上,加入进度的管理(注释部分的A、B、C 3 段),代码如下:

1234567891011121314151617181920212223242526272829303132333435363738        /// <summary>        /// 导出当前列表数据到Excel        /// </summary>        protected void ExportToExcel() {            using (var outputStm = new MemoryStream()) {                using (var document = new SLDocument()) {                    var fields = this.ExportedFields;                    //先输出表头                    for (var i = 0; i < fields.Count; i++) {                        document.SetCellValue(1, i + 1, fields[i].Label);                    }                    //输出内容                    if (GridControl != null) {                        var ps = ProgressService.GetInstance(FormHelper.GetString(Request.Form["download_key"])); // A:创建进度                        var f = GetFilter();                        var itemCount = 0;                        var source = GetGridSource(f, GridControl.GetSortExpression().ToOrder(CurrentDAL), int.MaxValue, 1, out itemCount);                        ps.Total = itemCount;   // B: 设置总进度                        var row = 2;                        object value = null;                        foreach (var item in source) {                            for (var col = 0; col < fields.Count; col++) {#if DEBUG                                System.Threading.Thread.Sleep(50);#endif                                value = item.GetType().GetProperty(fields[col].Field).GetValue(item, null);                                document.SetCellValue(row, col + 1, value);                            }                            ps.Elapsed += 1;    // C: 更新已经完成的进度                            row++;                        }                    }                    document.SaveAs(outputStm);                }                outputStm.Position = 0;                WebUtility.WriteFile(outputStm, outputStm.Length, this.Response, ExportedFileName + ".xlsx");            }        }

  最后附上效果图

居于Web的进度条实现思路(下载百分比)

 分类: Asp.netC#Html / JavaScript / CSS

相关推荐
python开发_常用的python模块及安装方法
adodb:我们领导推荐的数据库连接组件bsddb3:BerkeleyDB的连接组件Cheetah-1.0:我比较喜欢这个版本的cheeta…
日期:2022-11-24 点赞:878 阅读:9,492
Educational Codeforces Round 11 C. Hard Process 二分
C. Hard Process题目连接:http://www.codeforces.com/contest/660/problem/CDes…
日期:2022-11-24 点赞:807 阅读:5,907
下载Ubuntn 17.04 内核源代码
zengkefu@server1:/usr/src$ uname -aLinux server1 4.10.0-19-generic #21…
日期:2022-11-24 点赞:569 阅读:6,740
可用Active Desktop Calendar V7.86 注册码序列号
可用Active Desktop Calendar V7.86 注册码序列号Name: www.greendown.cn Code: &nb…
日期:2022-11-24 点赞:733 阅读:6,494
Android调用系统相机、自定义相机、处理大图片
Android调用系统相机和自定义相机实例本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显…
日期:2022-11-24 点赞:512 阅读:8,132
Struts的使用
一、Struts2的获取  Struts的官方网站为:http://struts.apache.org/  下载完Struts2的jar包,…
日期:2022-11-24 点赞:671 阅读:5,295