居于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& ...
随机推荐
- 81B
模拟 字符串必须先清零,要不会出现玄学的问题 #include<iostream> #include<cstdio> using namespace std; string s ...
- python安装失败0x80240017
安装KB2999226更新补丁后, 可以正常安装python3.5. 此更新包在vs2015的patch包里有.Microsoft下载中心也有,这里列出的适用于win7x86: Windows 7 更 ...
- UNIX命令,统计当前目录(含子目录)下所有后缀为.log的文件中ERROR出现的行数
shell程序如下所示: # cat xarg.txt #! /usr/bin/ksh for logfile in `find . -name "*.log*"` do echo ...
- 【HDU 5855】Less Time, More profit(网络流、最小割、最大权闭合子图)
Less Time, More profit Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/O ...
- 【CCCC天梯赛决赛】
cccc的天梯赛决赛,水题一样的水,中档题以上的还是没做出来.补了一下题,觉得其实也不是很难,主要是练的少. L2-1:红色预警 并查集 我做的时候想不到并查集,想到了也不一定做的出来,都是其实不难. ...
- 改变Yii2的默认路由
修改配置文件 return [ 'name'=>'AA后台', 'id' => 'app-backend', 'defaultRoute'=>'shop/index',//路由 …… ...
- 卸载移动硬盘出现 device is busy
umount /dev/sdb1 # device is busy fuser -m -v /dev/sdb1 # 查看 fuser -m -k /dev/sdb1 # 杀死进程
- BZOJ 1123: [POI2008]BLO
1123: [POI2008]BLO Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1030 Solved: 440[Submit][Status] ...
- (转)CSS的Sprites技术
Css Sprites 技术逐渐流行,各大网站上都可以看到它的身影. 但从本质上,Css Sprites 只是 Css 技术的一个使用小窍门,初学者也能快速上手. Css Sprites 简单解释: ...
- linux中sh基本语法
介绍:1 开头程序必须以下面的行开始(必须方在文件的第一行):#!/bin/sh 有人说是bash符号#!用来告诉系统它后面的参数是用来执行该文件的程序.在这个例子中我们使用/bin/sh来执行程序 ...