• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    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 ListAsyncResult> AsyncResults = new ListAsyncResult>();
            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 mvc 实现文件上传带进度条的思路与方法
    • asp.net文件上传带进度条实现案例(多种风格)
    • asp.net单文件带进度条上传的解决方案
    • asp.net(c#)开发中的文件上传组件uploadify的使用方法(带进度条)
    • asp.net 在客户端显示服务器端任务处理进度条的探讨
    • asp.net 生成静态页时的进度条显示
    • Asp.net基于ajax和jquery-ui实现进度条
    上一篇:HTTP错误500.19解决方法(定义了重复的节点)
    下一篇:Asp.net获取客户端IP常见代码存在的伪造IP问题探讨
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    Asp.Net 无刷新文件上传并显示进度条的实现方法及思路 Asp.Net,无,刷新,文件,上传,