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

(0)

相关推荐

  • asp.net(c#)开发中的文件上传组件uploadify的使用方法(带进度条)

    在Web开发中,有很多可以上传的组件模块,利用HTML的File控件的上传也是一种办法,不过这种方式,需要处理的细节比较多,而且只能支持单文件的操作.在目前Web开发中用的比较多的,可能uploadify(参考http://www.uploadify.com/)也算一个吧,不过这个版本一直在变化,他们的脚本调用也有很大的不同,甚至调用及参数都一直在变化,很早的时候,那个Flash的按钮文字还没法变化,本篇随笔主要根据项目实际,介绍一下3.1版本的uploadify的控件使用,这版本目前还是最新的

  • ASP.NET实现进度条效果

    我们先看下进度条效果 我点击了按钮后他会显示进度页面,进度完成后,进度条消失,其实也是比较简单的了. 我们需要一个进度条代码文件ProgressBar.htm(注意:是没有head这些标签的) <script language="javascript"> function SetPorgressBar(pos) { //设置进度条居中 var screenWidth = document.body.offsetWidth; ProgressBarSide.style.wid

  • asp.net mvc 实现文件上传带进度条的思路与方法

    前言 文件上传与下载的操作在实际项目中经常是很重要的一个内容,在使用ASP.NET Web Form的时候我们可以使用诸多的服务器控件,FileIpload就是其中之一,但是在ASP.NET不建议使用那些服务器控件,因为那样违反三层架构原则.最近参考网络资料,学习了ASP.NET MVC如何上传文件. 而这篇文章主要重点是asp.net mvc 实现文件上传带进度条,下面来一起看看吧. 实现思路 ajax异步上传文件,且开始上传文件的时候启动轮询来实时获取文件上传进度.保存进度我采用的是memc

  • asp.net单文件带进度条上传的解决方案

    最近做项目中遇到很多问题,比如带进度条的文件上传,看了网上很多资料还没找到真正意义上的ASP.NET实现进度条上传(可能是我没找到),下面我来跟大家分享一下我实现的这个程序. 首先看下界面效果,当然你可以完全修改界面为你自己所用. 先解释一下这个程序,该程序采用了jquery框架,实现了小文件上传,不超过80Mb,可以在web.config文件中进行相应的配置,但是有个最大值,具体需要查看msdn.开发环境采用visual studio 2013 .net framework 4.5,运行的时候

  • asp.net 生成静态页时的进度条显示

    asp.net如何生成静态页,请参考下面的文章:http://www.jb51.net/article/18175.htm而我们用模拟的话,只需要让线程延迟执行就可以了.比如下面的代码: 复制代码 代码如下: for (int i = 0; i < 10; i++) { DateTime startTime = DateTime.Now; Response.Write(i + "-------------执行时间:" + startTime.ToString()+"&l

  • asp.net 在客户端显示服务器端任务处理进度条的探讨

    下面就是采用静态变量的方法实现的: 复制代码 代码如下: <%@ Page Language="C#" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1 -transitional.dtd"> <script runat="server"

  • asp.net web大文件上传带进度条实例代码

    复制代码 代码如下: using System; using System.Data; using System.Configuration; using System.Collections; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using Syste

  • ASP.NET实现用图片进度条显示投票结果

    一个投票功能模块少不了查看投票结果,用进度条显示各个投票结果可以起到一目了然的效果.以下是我的方法,请大家不吝赐教:  1:做一张图片用于做进度条,只需要很小的一个图片就可以了,如高20px,宽1px. 2:在要显示进度条的单元格中插入image控件,其imageUrl设置为已做好的图片的位置. 3:用一个dataReader对象dr保存取出的各项票数,用一个int 型变量sum保存取出的总票数,各项分别再定义一个double型变量用来保存单项票数除以(/)总票数的结果(小数),再定义一个int

  • 利用Asp.Net回调机制实现进度条

    其效果如下:首先,在HTML文档中加入如下代码:<div> <table class="statusTable"> <tr> <td id="progress1"> </td> <td id="progress2"> </td> <td id="progress3"> </td> <td id="prog

  • asp.net文件上传带进度条实现案例(多种风格)

    先饱饱眼福: 在之前的文章中也有类似带进度条文件传送的案例,大家可以翻阅之前的文章对知识点进行扩充. 部分代码: <%@ Page Language="C#" %> <%@ Register Assembly="MattBerseth.WebControls.AJAX" Namespace="MattBerseth.WebControls.AJAX.Progress" TagPrefix="mb" %>

随机推荐