当前位置: 移动技术网 > IT编程>开发语言>.net > Asp.Net 无刷新文件上传并显示进度条的实现方法及思路

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

2017年12月12日  | 移动技术网IT编程  | 我要评论

鸿钧之师,大宋王朝赵匡胤电视剧,乱世激流txt

相信通过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

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网