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. editplus-使用正则表达式替换每行首字母和尾字母

    打开EditPlus,输入多行数据,快捷键ctrl+h 打开替换窗口,选择“正则表达式”替换 行首批量添加   查找"^" 替换为“我是行首aaa” 行尾批量添加   查找&quo ...

  2. jquery.ui.widget详解

    案例详解 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <titl ...

  3. for 循环 正方形

    <?php//================================正方形//for($q = 1; $q <= 5; $q ++ ){//    for($z =1; $z & ...

  4. 又爱又恨系列之枚举enum

    其实枚举挺简单的,只不过以前没好好学,所以不知道这个东西,恩,现在梳理一下 整体而言,首先枚举是一个数据类型,这个数据类型和结构体有点像 可以分为三个层次 1.枚举数据类型定义 第一种:enum 枚举 ...

  5. js-JavaScript高级程序设计学习笔记9

    依然第十三章 事件 1.页面上的所有元素都支持鼠标事件,除了mouseenter和mouseleave,所有鼠标事件都会冒泡. 2.修改键:shift.ctrl.alt.meta.四个属性表示修改键的 ...

  6. 【BZOJ-4524】伪光滑数 堆 + 贪心 (暴力) [可持久化可并堆 + DP]

    4524: [Cqoi2016]伪光滑数 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 183  Solved: 82[Submit][Status] ...

  7. Uva11538 排列组合水题

    画个图就很容易推出公式: 设mn=min(m,n),mx=max(m,n) 对角线上: 横向:m*C(n,2) 纵向:n*C(m,2) 因为所有的C函数都是只拿了两个,所以可以优化下.不过不优化也过了 ...

  8. CF 115B Lawnmower(贪心)

    题目链接: 传送门 Lawnmower time limit per test:2 second     memory limit per test:256 megabytes Description ...

  9. 《C陷阱与缺陷》杂记

    第一章 词法"陷阱" 1.4整型常量 如果一个整型常量的第一个字符是数字0,那么该常量将被视作八进制数.因此,10与010的含义截然不同.需要注意这种情况,有时候在上下文为了格式& ...

  10. ARPSpoofing教程(三) - 捕获数据包

    1: #include"pcap.h" 2: //每次捕获到数据包时,libpcap都会自动调用这个回调函数 3: void packet_handler(u_char *para ...