好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

Asp.Net实现无刷新文件上传并显示进度条(非服务器控件实现)

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

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

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

源代码下载: UploadFileDemo.rar

  

分类:  Asp.Net 杂谈

标签:  Asp.Net无刷新上传文件 ,  Asp.Net上传文件并显示进度条 ,  Asp.net非服务器控件实现文件上传

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于Asp.Net实现无刷新文件上传并显示进度条(非服务器控件实现)的详细内容...

  阅读:48次