Asp.Net 无刷新文件上传并显示进度条的实现方法及思路
相信通过Asp.Net的服务器控件上传文件在简单不过了,通过AjaxToolkit控件实现上传进度也不是什么难事,为什么还要自己辛辛苦苦来 实现呢?我并不否认”拿来主义“,只是我个人更喜欢凡是求个所以然。本篇将阐述通过Html,IHttpHandler和 IHttpAsyncHandler实现文件上传和上传进度的原理,希望对你有多帮助。
效果图:
本文涉及到的知识点:
1.前台用到Html,Ajax,JQuery,JQuery UI
2.后台用到一般处理程序(IHttpHandler)和一般异步处理程序(IHttpAsyncHandler),并涉及到”推模式“
一、创建Html网页
1、在创建的Web工程中添加一个Html文件,命名为UploadFile.htm,在头文件中引入JQuery,JQuery UI
<link href="Styles/jquery-ui-1.8.16.custom.css" rel="stylesheet" type="text/css" />
<script src="Scripts/jquery-1.6.2.min.js" type="text/javascript"></script>
<script src="Scripts/jquery-ui-1.8.16.custom.min.js" type="text/javascript"></script>
2、关于无刷新文件上传
通过Ajax是不能上传文件的,无刷新上传是靠隐藏的iframe来实现的
<form id="form" target = "frameFileUpload" enctype="multipart/form-data">
<div id="progressBar" style="font-size: 1em;"></div>
<input type="file" id="fileUpload" name="fileUpload" /><span id="progressValue"></span>
<iframe id="frameFileUpload" name="frameFileUpload" style="display:none;" ></iframe>
<br />
<input type="submit" value="上传" id = "submit"/>
</form>
要将form标签的target属性设置为iframe的id,当然别忘了将form的enctype设置为multipart/form-data
<div id="progressBar" style="font-size: 1em;"></div>
是用来显示上传文件时的进度条
在JS中加入如下处理:
<script type="text/javascript">
$(function () {
$("#submit").button();
$("#fileUpload").button();
});
</script>
此时效果:
二、实现文件上传
添加一个一般处理程序,命名为UploadFileHandler.ashx
public void ProcessRequest(HttpContext context)
{
//如果提交的文件名是空,则不处理
if (context.Request.Files.Count == 0 || string.IsNullOrWhiteSpace(context.Request.Files[0].FileName))
return;
//获取文件流
Stream stream = context.Request.Files[0].InputStream;
//获取文件名称
string fileName = Path.GetFileName(context.Request.Files[0].FileName);
//声明字节数组
byte[] buffer;
//为什么是4096呢?这是操作系统中最小的分配空间,如果你的文件只有100个字节,其实它占用的空间是4096个字节
int bufferSize = 4096;
//获取上传文件流的总长度
long totalLength = stream.Length;
//已经写入的字节数,用于做上传的百分比
long writtenSize = 0;
//创建文件
using (FileStream fs = new FileStream(@"C:\" + fileName, FileMode.Create, FileAccess.Write))
{
//如果写入文件的字节数小于上传的总字节数,就一直写,直到写完为止
while (writtenSize < totalLength)
{
//如果剩余的字节数不小于最小分配空间
if (totalLength - writtenSize >= bufferSize)
{
//用最小分配空间创建新的字节数组
buffer = new byte[bufferSize];
}
else
//用剩余的字节数创建字节数组
buffer = new byte[totalLength - writtenSize];
//读取上传的文件到字节数组
stream.Read(buffer, 0, buffer.Length);
//将读取的字节数组写入到新建的文件流中
fs.Write(buffer, 0, buffer.Length);
//增加写入的字节数
writtenSize += buffer.Length;
//计算当前上传文件的百分比
long percent = writtenSize * 100 / totalLength;
}
}
}
在form中添加action和method属性,修改之后的
<form action="UploadFileHandler.ashx" method="post" id="form" target = "frameFileUpload" enctype="multipart/form-data">
这样文件上传就完成了。
三、实现文件上传的进度显示
我的思路:
文件上传的处理过程中,是不可以在处理过程中将信息传回客户端的,只有当所有的处理都完毕之后才会传回客户端,所以如果是在上面的处理程序中写 入context.Response.Write(percent);是不可能得到处理的过程,只能等到处理结束后,客户端一次性得到所有的值。
要想得到处理过程中的值,我的解决是这样,在文件上传时,要开启另一个请求,来获取进度信息。而这个请求是异步的,我指的是客户端异步请求和服 务端异步处理。因为要涉及到两个不同的请求处理程序之间信息的传递,将"处理文件上传的程序"得到的进度信息传递给"处理进度请求的程序",而"处理进度 请求的处理程序"要依赖于"处理文件上传的处理程序"。处理图:
首先客户端同时(几乎是)发出两个请求,一个是文件上传,一个是进度请求。由于"处理请求进度的程序"是异步处理的,当该程序没有信息发给客户 端时,我们让它处于等待状态,这里有点像Tcp,这样客户端跟服务器就一直处于连接状态。当"处理文件上传的程序"开始处理时,通过把进度值赋值给"处理 请求进度程序"的异步操作的状态,并触发"处理请求进度的程序"返回值给客户端。客户端获取进度值,并处理。这样一次请求进度值的请求就结束了,我们知道 服务器是不会主动给客户端发送信息的,只有客户端请求,服务器才会响应。显然,要想在文件保存的过程中向客户端发送进度信息,客户端得到每得到一个返回结 果,都是一次请求。为了得到连续的请求值,客户端再向"处理请求进度的程序"发出请求,依次循环,知道文件上传结束。
技术实现:
异步处理用到接口IHttpAsyncHandler,新建一个一般处理程序,命名为RequestProgressAsyncHandler.ashx,将默认的接口改为IHttpAsyncHandler
public class RequestProgressAsyncHandler : IHttpAsyncHandler
{
public void ProcessRequest(HttpContext context)
{
}
public bool IsReusable
{
get
{
return false;
}
}
#region IHttpAsyncHandler 成员
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
throw new NotImplementedException();
}
public void EndProcessRequest(IAsyncResult result)
{
throw new NotImplementedException();
}
#endregion
}
BeginProcessRequest和EndProcessRequest是两个核心的方法,其他的两个不用处理。当该处理程序处理请求 时,BeginProcessRequest是第一个被调用的函数,返回一个包含异步状态信息的对象,该对象是IAsyncResult类型,是实现异步 的关键,用于控制什么时候调用EndProcessRequest来结束处理程序的等待状态,BeginProcessRequest被调用之后,程序就 处于等待状态。EndProcessRequest是在结束请求时的处理函数,通过该函数可以向客户端写入信息。
实现接口IAsyncResult
public class AsyncResult : IAsyncResult
{
// 标示异步处理的状态
private bool isComplete = false;
//保存异步处理程序中的Http上下文
private HttpContext context;
//异步回调的委托
private AsyncCallback callback;
/// <summary>
/// 获取或设置保存下载文件的百分比数值部分
/// </summary>
public long PercentNumber;
public AsyncResult(HttpContext context, AsyncCallback callback)
{
this.context = context;
this.callback = callback;
}
/// <summary>
/// 向客户端写入信息
/// </summary>
public void Send()
{
this.context.Response.Write(PercentNumber);
}
/// <summary>
/// 完成异步处理,结束请求
/// </summary>
public void DoCompleteTask()
{
if (callback != null)
callback(this);//会触发处理程序中的EndProcessRequest函数,结束请求
this.isComplete = true;
}
#region IAsyncResult 成员
public object AsyncState
{
get { return null; }
}
public System.Threading.WaitHandle AsyncWaitHandle
{
get { return null; }
}
public bool CompletedSynchronously
{
get { return false; }
}
public bool IsCompleted
{
get { return isComplete; }
}
#endregion
}
修改 RequestProgressAsyncHandler.ashx文件:
public class RequestProgressAsyncHandler : IHttpAsyncHandler
{
/// <summary>
/// 保存异步处理状态信息的集合
/// </summary>
public static List<AsyncResult> AsyncResults = new List<AsyncResult>();
public void ProcessRequest(HttpContext context)
{
}
public bool IsReusable
{
get
{
return false;
}
}
#region IHttpAsyncHandler 成员
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
{
AsyncResult result = new AsyncResult(context, cb);
AsyncResults.Add(result);
return result;
}
public void EndProcessRequest(IAsyncResult result)
{
//保证集合中只用一个元素
AsyncResults.Clear();
AsyncResult ar = (AsyncResult)result;
ar.Send();
}
#endregion
}
在UploadFileHandler.ashx添加如下代码:
private static void SendPercentToClient(long percent)
{
//当上传完毕后,保证处理程序能向客户端传回
while (RequestProgressAsyncHandler.AsyncResults.Count == 0 && percent == 100)
{
}
//因为本处理程序和"处理请求进度的程序"是并发的,不能保证RequestProgressAsyncHandler.AsyncResults一定含有子项
if (RequestProgressAsyncHandler.AsyncResults.Count != 0)
{
RequestProgressAsyncHandler.AsyncResults[0].PercentNumber = percent;
RequestProgressAsyncHandler.AsyncResults[0].DoCompleteTask();
}
}
在函数ProcessRequest中加入以上方法:
...
...
//计算当前上传文件的百分比
long percent = writtenSize * 100 / totalLength;
SendPercentToClient(percent);
服务端OK!修改客户端,添加JS处理函数:
function RequestProgress() {
$.post("RequestProgressAsyncHandler.ashx", function (data, status) {
if (status == "success") {
$("#progressValue").text(data + "%");
data = parseInt(data);
$("#progressBar").progressbar({ value: data });//JQuery UI 设置进度条值
//如果进度不是 100,则重新请求
if (data != 100) {
RequestProgress();
}
}
});
}
在form中添加事件omsubmit的处理函数为RequestProgress
<form action="UploadFileHandler.ashx" onsubmit = "RequestProgress();" method="post" id="form" target = "frameFileUpload" enctype="multipart/form-data">
补充几点:
1.默认Asp.Net允许的上传文件的大小是4M,可以在Web.config中修改其大小限制
<system.web>
<httpRuntime maxRequestLength="444444"/>
</system.web>
maxRequestLength的单位是KB
Asp.Net 无刷新文件上传并显示进度条的实现方法及思路的更多相关文章
- Asp.Net实现无刷新文件上传并显示进度条(非服务器控件实现)(转)
Asp.Net实现无刷新文件上传并显示进度条(非服务器控件实现) 相信通过Asp.Net的服务器控件上传文件在简单不过了,通过AjaxToolkit控件实现上传进度也不是什么难事,为什么还要自己辛辛苦 ...
- ie8实现无刷新文件上传
ie8由于无法使用FormData,想要无刷新上传文件就显得比较麻烦.这里推荐使用jQuery-File-Upload插件,它能够很方便的解决ie8无刷新文件上传问题.(最低兼容到ie6) jQuer ...
- 使用PHP和HTML5 FormData实现无刷新文件上传教程
无刷新文件上传是一个常见而又有点复杂的问题,常见的解决方案是构造 iframe 方式实现. 在 HTML5 中提供了一个 FormData 对象 API,通过 FormData 可以方便地构造一个表单 ...
- 【JS】ajax 实现无刷新文件上传
一.摘要 最近在做个东西,需要实现页面无刷新文件上传,目前看到的方法有两种 1) 通过隐藏iframe 实现页面无刷新,适用于不关心上传结果 <form target="hiddenF ...
- 实用ExtJS教程100例-009:ExtJS Form无刷新文件上传
文件上传在Web程序开发中必不可少,ExtJS Form中有一个filefield字段,用来选择文件并上传.今天我们来演示一下如何通过filefield实现ExtJS Form无刷新的文件上传. 首先 ...
- SpringMVC ajax技术无刷新文件上传下载删除示例
参考 Spring MVC中上传文件实例 SpringMVC结合ajaxfileupload.js实现ajax无刷新文件上传 Spring MVC 文件上传下载 (FileOperateUtil.ja ...
- java进行文件上传,带进度条
网上看到别人发过的一个java上传的代码,自己写了个完整的,附带源码 项目环境:jkd7.tomcat7. jar包:commons-fileupload-1.2.1.jar.commons-io-1 ...
- 【原创】用JAVA实现大文件上传及显示进度信息
用JAVA实现大文件上传及显示进度信息 ---解析HTTP MultiPart协议 (本文提供全部源码下载,请访问 https://github.com/grayprince/UploadBigFil ...
- Javascrpt无刷新文件上传
最近工作中遇到上传文件问题,主要需求是一步点击上传,兼容ie8+,当时用的dojox/form/uploader控件,这两天扒了一下源码,明白了原理拿出来分享一下. 总体思路如下: 1.对于支持XML ...
随机推荐
- [Python爬虫] 之二十四:Selenium +phantomjs 利用 pyquery抓取中广互联网数据
一.介绍 本例子用Selenium +phantomjs爬取中广互联网(http://www.tvoao.com/select.html)的资讯信息,输入给定关键字抓取资讯信息. 给定关键字:数字:融 ...
- 【后台管理系统】—— Ant Design Pro入门学习&项目实践笔记(三)
前言:前一篇记录了[后台管理系统]目前进展开发中遇到的一些应用点,这一篇会梳理一些自己学习Ant Design Pro源码的功能点.附:Ant Design Pro 在线预览地址. Dashboard ...
- Zend Framework(一) windows8.1下配置zend framework1.12
windows8.1下配置zend framework1.12配置步骤: 1. 下载 zend framework1.12库 2. 创建zend frameworkproject 2 ...
- CSS/JavaScript hacks,browserhacks使用
1.网址 http://browserhacks.com/ 2.使用 (1)JavaScript Hacks 浏览器js判断 (2)条件注释hack (3)Media Query Hacks 媒体查询 ...
- 在ubuntu10.04 下将360wifi当无线网卡使用
通过百度“360wifi linux“ 已经有很多解决方案.主要过程是从网上下载mt7601驱动包,编译出一个内核模块后,再通过modprobe 添加模块.下面描述的是基于我本机的特点所做的额外工作. ...
- 谷歌Cartographer学习 -快速安装测试
参考资料:https://www.cnblogs.com/hitcm/p/5939507.html PC下面进行安装: 遇到的问题如下 1.首先安装ceres solver 在编译的时候,如果是低配的 ...
- C语言-十进制转换为二进制函数
char * itobs(int num, char * str) { int i; * sizeof(int); ; i >= ; i--, num >>= ) { str[i] ...
- Centos开机自己主动挂载windows中的ntfs磁盘
装了windows和centos双系统后,发如今centos中无法进入windows中的磁盘,更不要说查看磁盘里的文件了! 原来默认情况下.centos不支持Widows NTFS硬盘分区读写,要想把 ...
- springboot学习(四) 日志管理
1.简介 Spring Boot内部日志系统使用的是Commons Logging,但开放底层的日志实现.默认为会Java Util Logging, Log4J, Log4J2和Logback提供配 ...
- 一次httpserver优化的经验和教训(silverlight游戏 - 金庸群侠传X0.5上线记)
金X因为被推荐到ACFUN游戏排行第一名.并同一时候在17YY.7K7K.U77.17173等各大小游戏站点上线.迎来了在线用户数量的爆炸式增长.眼下各大站点使用外链方式.也就是实际链接到金X官网的s ...