通过Asp.Net的服务器控件上传文件总结

时间:2020-11-09 13:15:35 ASP 我要投稿

通过Asp.Net的服务器控件上传文件总结

  相信通过Asp.Net的服务器控件上传文件在简单不过了,通过AjaxToolkit控件实现上传进度也不是什么难事,为什么还要自己辛辛苦苦来 实现呢?我并不否认”拿来主义“,只是我个人更喜欢凡是求个所以然。本篇将阐述通过Html,IHttpHandler和 IHttpAsyncHandler实现文件上传和上传进度的原理,希望对你有多帮助。

通过Asp.Net的服务器控件上传文件总结

  效果图:

  本文涉及到的知识点:

  1.前台用到Html,Ajax,JQuery,JQuery UI

  2.后台用到一般处理程序(IHttpHandler)和一般异步处理程序(IHttpAsyncHandler),并涉及到”推模式“

  一、创建Html网页

  1、在创建的Web工程中添加一个Html文件,命名为UploadFile.htm,在头文件中引入JQuery,JQuery UI

  复制代码 代码如下:

  二、实现文件上传

  添加一个一般处理程序,命名为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属性,修改之后的

  复制代码 代码如下:

  这样文件上传就完成了。

  三、实现文件上传的进度显示

  我的思路:

  文件上传的处理过程中,是不可以在处理过程中将信息传回客户端的,只有当所有的处理都完毕之后才会传回客户端,所以如果是在上面的处理程序中写 入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;

  ///

  /// 获取或设置保存下载文件的百分比数值部分

  ///

  public long PercentNumber;

  public AsyncResult(HttpContext context, AsyncCallback callback)

  {

  this.context = context;

  this.callback = callback;

  }

  ///

  /// 向客户端写入信息

  ///

  public void Send()

  {

  this.context.Response.Write(PercentNumber);

  }

  ///

  /// 完成异步处理,结束请求

  ///

  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

  {

  ///

  /// 保存异步处理状态信息的集合

  ///

  public static ListAsyncResults = new List();

  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

  复制代码 代码如下:

  补充几点:

  1.默认Asp.Net允许的上传文件的大小是4M,可以在Web.config中修改其大小限制

  复制代码 代码如下:

  maxRequestLength的单位是KB

  2.在IE 8.0测试中,在文件上传完毕后,状态栏还处于请求中

  反正不是后台还在请求,这个放心,只要把鼠标在按钮和浏览上面来回移动几下就没了,可能是JQuery UI 的问题。FF和Chrom下没这个问题,就是显示效果会有点差,但是上传没问题的。

  源代码下载:UploadFileDemo.rar

【通过Asp.Net的服务器控件上传文件总结】相关文章:

1.关于ASP.NET DropDownList控件的使用方法

2.Servlet上传文件注意事项

3.如何设置服务器asp.net权限

4.asp.net中将js的返回值赋控件的小例子

5.asp.net 操作INI文件读写类实例代码

6.ASP的chr(0)文件上传漏洞原理及解决方案

7.关于ASP.NET中TextBox使用Ajax控件显示日期不全的问题解决方法

8.有关dz asp.net论坛中函数--根据Url获得源文件内容