居于Web的进度条实现思路(下载百分比)
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 { get; set; } /// <summary> /// 获取或设置已经完成的进度 /// </summary> public int Elapsed { get; set; } /// <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的进度条实现思路(下载百分比)的更多相关文章
- HTML5效果:Canvas 实现圆形进度条并显示数字百分比
实现效果 1.首先创建html代码 <canvas id="canvas" width="500" height="500" styl ...
- Java web实时进度条整个系统共用(如java上传、下载进度条、导入、导出excel进度条等)
先上图: 文件上传的: 2017-05-04再次改进.在上传过程中用户可以按 Esc 来取消上传(取消当前上传,或者是全部上传)... 2019-03-26更新进度条显示体验 从服务器上压缩下载: 从 ...
- ASP.NET技巧:教你制做Web实时进度条
网上已经有很多Web进度条的例子,但是很多都是估算时间,不能正真反应任务的真实进度.我自己结合多线程和ShowModalDialog制做了 一个实时进度条,原理很简单:使用线程开始长时间的任务,定义一 ...
- C# winform带进度条的图片下载
代码如下: public partial class FrmMain : Form { public FrmMain() { InitializeComponent(); } private void ...
- Java web实时进度条整个系统共用(如java上传进度条、导入excel进度条等)
先上图: 这上文件上传的: 这是数据实时处理的: 1:先说说什么是进度条:进度条即计算机在处理任务时,实时的,以图片形式显示处理任务的速度,完成度,剩余未完成任务量的大小,和可能需要处理时间,显示方式 ...
- js进度条源码下载—js进度条代码
现在很多网站会用到进入网站特效,到网页没有加载完成的时候,会有一个loding特效,加载完了之后才能看到页面,今天就带着做一个js进度条效果,今天要做的效果是纯js进度条加载,没有用到框架,方便大家进 ...
- .net网站的文件上传读取进度条和断点下载
文件上传到服务器时的进度读取 //调整上传配置 AdapterInfo(info); UpfileResult result = new UpfileResult(); try { //直接使用req ...
- Android有进度条异步任务下载图片
首先在AndroidMainifest中添加上网权限 ? 1 <uses-permission android:name="android.permission.INTERNET&qu ...
- Web报表进度条显示
创建插件 <script src="../CreateControl.js" type="text/javascript"></script& ...
随机推荐
- 史上最全最强SpringMVC详细示例实战教程
一.SpringMVC基础入门,创建一个HelloWorld程序 1.首先,导入SpringMVC需要的jar包. 2.添加Web.xml配置文件中关于SpringMVC的配置 1 2 3 4 5 6 ...
- JSon 事件格式化
JS~json日期格式化 起因 对于从C#返回的日期字段,当进行JSON序列化后,在前台JS里显示的并不是真正的日期,需要格式化时间 实现 function ChangeDateFormat(js ...
- bzoj 1257
商最多有sqrt(n)个. #include<iostream> #include<cstdio> #include<cstring> #include<al ...
- springMVC-配置Bean
配置Bean - 配置形式:基于xml文件方式,基于注解的方式- bean的配置方式:通过全类名(反射),通过工厂方法(静态工厂方法和实例工厂方法),FactoryBean依赖注入的方方式,属性注入和 ...
- 在数据库中如果组合主键(假设为stuID和stuName)存在则更新,不存在则新增
这是今天在项目中遇到的问题,后来查了一下,有的网友说可以用存储过程,但自己现在还不会用,所以下记载下来,做为学习存贮过程的引子. 现在是在java中实现了这个if的逻辑,
- 洛谷P1726 上白泽慧音
题目描述 在幻想乡,上白泽慧音是以知识渊博闻名的老师.春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄.因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点.人间 ...
- Mysql Index、B Tree、B+ Tree、SQL Optimization
catalog . 引言 . Mysql索引 . Mysql B/B+ Tree . Mysql SQL Optimization . MySQL Query Execution Process 1. ...
- Swift Swift中的反射
Swift的反射机制是基于一个叫 Mirror 的 struct 来实现的,其内部有如下属性和方法: let children: Children //对象的子节点. displayStyle: Mi ...
- os和sys模块
sys模块 sys模块主要是用于提供对python解释器相关的操作 函数 sys.argv #命令行参数List,第一个元素是程序本身路径 sys.path #返回模块的搜索路径,初始化时使用PYTH ...
- 帝国cms搜索表单用法
还有一些没有测试,用到了再补充. <form action="[!--news.url--]e/search/index.php" method="post&quo ...