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

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

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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,后台代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 为例,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
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 段),代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
       /// <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的进度条实现思路(下载百分比)的更多相关文章

  1. HTML5效果:Canvas 实现圆形进度条并显示数字百分比

    实现效果 1.首先创建html代码 <canvas id="canvas" width="500" height="500" styl ...

  2. Java web实时进度条整个系统共用(如java上传、下载进度条、导入、导出excel进度条等)

    先上图: 文件上传的: 2017-05-04再次改进.在上传过程中用户可以按 Esc 来取消上传(取消当前上传,或者是全部上传)... 2019-03-26更新进度条显示体验 从服务器上压缩下载: 从 ...

  3. ASP.NET技巧:教你制做Web实时进度条

    网上已经有很多Web进度条的例子,但是很多都是估算时间,不能正真反应任务的真实进度.我自己结合多线程和ShowModalDialog制做了 一个实时进度条,原理很简单:使用线程开始长时间的任务,定义一 ...

  4. C# winform带进度条的图片下载

    代码如下: public partial class FrmMain : Form { public FrmMain() { InitializeComponent(); } private void ...

  5. Java web实时进度条整个系统共用(如java上传进度条、导入excel进度条等)

    先上图: 这上文件上传的: 这是数据实时处理的: 1:先说说什么是进度条:进度条即计算机在处理任务时,实时的,以图片形式显示处理任务的速度,完成度,剩余未完成任务量的大小,和可能需要处理时间,显示方式 ...

  6. js进度条源码下载—js进度条代码

    现在很多网站会用到进入网站特效,到网页没有加载完成的时候,会有一个loding特效,加载完了之后才能看到页面,今天就带着做一个js进度条效果,今天要做的效果是纯js进度条加载,没有用到框架,方便大家进 ...

  7. .net网站的文件上传读取进度条和断点下载

    文件上传到服务器时的进度读取 //调整上传配置 AdapterInfo(info); UpfileResult result = new UpfileResult(); try { //直接使用req ...

  8. Android有进度条异步任务下载图片

    首先在AndroidMainifest中添加上网权限 ? 1 <uses-permission android:name="android.permission.INTERNET&qu ...

  9. Web报表进度条显示

    创建插件 <script src="../CreateControl.js" type="text/javascript"></script& ...

随机推荐

  1. 网络流 POJ2112

    题意:K个产奶机,C头奶牛,每个产奶机最多可供M头奶牛使用:并告诉了产奶机.奶牛之间的两两距离Dij(0<=i,j<K+C). 问题:如何安排使得在任何一头奶牛都有自己产奶机的条件下,奶牛 ...

  2. Spring mvc-异常javax.servlet.ServletException: Could not resolve view with name 'xxx' in servlet with name 'spring'

    最近使用spring mvc开发项目,遇到一个问题: javax.servlet.ServletException: Could not resolve view with name 'ok' in ...

  3. 18 BufferedReader使用方法

    生成BufferedReader对象的方法: BufferedReader in=new BufferedReader(new FileReader("foo.in")); imp ...

  4. bzoj4403: 序列统计

    我们很容易发现答案是C(R-L+N+1,N)-1 然后用一下lucas定理就行了 #include <iostream> #include <cstdio> #include ...

  5. 【bzoj2120】 数颜色

    http://www.lydsy.com/JudgeOnline/problem.php?id=2120 (题目链接) 题意 给出一个n个数,m个询问,每次询问一个区间或修改一个数,求区间内不同的数有 ...

  6. ConvertHelper类

    /// <summary> /// 处理数据类型转换,数制转换.编码转换相关的类 /// </summary> public sealed class ConvertHelpe ...

  7. UOJ149 【NOIP2015】子串

    本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! [问题描述]有两个仅包含小写英文字母的字符串 A ...

  8. DIV横排/竖排滚动(white-space/::-webkit-scrollbar的使用技巧以及display: inline-block的选择)支持手势

    参考微信的京东客户端http://wqs.jd.com/,实现DIV的横排滚动,且支持手势,并且不会出现滚动跳,效果如下: 但是观察其div结构是没有使用任何JS去实现. 一.实现上DIV的横排 观察 ...

  9. CCNET+ProGet+Windows Batch搭建全自动的内部包打包和推送及管理平台

    所要用的工具: 1.CCNET(用于检测SVN有改动提交时自动构建,并运行nuget的自动打包和推送批处理) 2.ProGet(目前见到最好用的nuget内部包管理平台) 3.Windows Batc ...

  10. bzoj1801[AHOI2009]CHESS中国象棋

    题意:在棋盘上放一些炮使得它们不互相攻击.其实就是一行/一列最多放两个. 50分的数据中n,m至少有一个不超过8,比较直接的想法是对n/m中较小的一维做状态压缩,状态f[i][S1][S2]表示在前i ...