• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

基于IHttpAsyncHandler的实时大文件传送器 Asp.Net实现无刷新文件上传并显示 ...

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

在日常工作中,有时候需要到远程服务器上部署新版本的系统,由于远程服务器出于外网,所以每次都要开QQ连接,非常麻烦。索性就研究了下IHttpasyncHandler,并结合Juqery ProgressBar,打造了一款大文件传送器。其基本原理就是首先在客户端将大文件分段拆分,然后写入内存流,最后发送到服务器上。在上传的同时,会利用异步Handler来获取当前进度并推送到前台显示。图示效果如下(当前缓存设置为5000字节大小):

图示结果

(图1,传送到36%的时候)

(图2,传送到100%的时候)

 

异步Handler设计

下面来说说具体的实现方式,首先来说说实时通知这块:

using System;
using System.Collections.Generic;
using System.Web;
using AsyncThermometerDeamon.Handlers.Code;

namespace AsyncThermometerDeamon.Handlers
{
    public class FileUploadWatcher : IHttpAsyncHandler
    {
        public static List<AsyncResultDaemon> asyncResults = new List<AsyncResultDaemon>();
        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData)
        {
            AsyncResultDaemon asyncResult = new AsyncResultDaemon(context,cb,extraData);
            asyncResults.Add(asyncResult);
            return asyncResult;
        }

        public void EndProcessRequest(IAsyncResult result)
        {
            asyncResults.Clear();
            AsyncResultDaemon aResult = result as AsyncResultDaemon;
            aResult.Send();
        }

        public bool IsReusable
        {
            get { return false; }
        }

        public void ProcessRequest(HttpContext context)
        {
            throw new NotImplementedException();
        }
    }
}

在程序中,首先申明了一个List容器,以便保存当前的请求。然后当有请求进来的时候,BeginProcessRequest会将当前请求进行初始化,然后加入到List容器中,最后将执行结果返回。

而在EndProcessRequest函数中,一旦当前的操作完成,将会触发此函数推送当前的进度数据到前台。

这里我们来看一下AsyncResultDaemon类:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace AsyncThermometerDeamon.Handlers.Code
{
    public class AsyncResultDaemon:IAsyncResult
    {
        public AsyncResultDaemon(HttpContext context, AsyncCallback cb,object extraData)
        {
            this.context = context;
            this.cb = cb;
        }

        private HttpContext context;
        private AsyncCallback cb;
        private object extraData;
        public long percent;
        public bool isCompleted = false;

        public void Send()
        {
            context.Response.Write(percent);
        }

        public void CompleteTask()
        {
            if (cb != null)
            {
                cb(this);
                this.isCompleted = true;
            }
        }

        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 isCompleted; }
        }
    }
}


这个类很简单,继承自IAsyncResult接口,主要用来返回异步执行结果的。当异步执行任务完毕后,其他函数可以通过调用CompleteTask方法来抛出任务完成事件,这个抛出的事件将会被EndProcessRequest接住,进而推送实时进度通知。

 

文件上传Handler设计

说完了异步Handler,再来说一说文件上传Handler:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.IO;

namespace AsyncThermometerDeamon.Handlers
{
    public class FileUpload : IHttpHandler
    {
        //设置发送的缓冲大小
        private int bufferSize = 5000;

        public void ProcessRequest(HttpContext context)
        {
            //得到文件全路径及文件名称
            string filePath = context.Request.QueryString["path"];
            string fileName = Path.GetFileName(filePath);
            byte[] byteToSend;

            FileStream fs = new FileStream(filePath, FileMode.Open);
            fs.Position = 0;
            long totalBytesLength = fs.Length;

            //文件分多少批发送
            long totalBatch = 0;
            if (totalBytesLength % bufferSize == 0)
                totalBatch = totalBytesLength / bufferSize;
            else
                totalBatch = totalBytesLength / bufferSize + 1;

            //遍历
            for (int i = 0; i < totalBatch; i++)
            {
                //设置当前需要获取的流的位置
                fs.Position = i * bufferSize;
                //如果是最后一批流数据
                if (i == totalBatch - 1)
                {
                    long lastBatchLength = totalBytesLength = totalBytesLength - i * bufferSize;
                    byteToSend = new byte[lastBatchLength];
                    fs.Read(byteToSend, 0, (int)lastBatchLength);
                }
                else
                {
                    byteToSend = new byte[bufferSize];
                    fs.Read(byteToSend, 0, bufferSize);
                }

                //避免闭包
                int j = i;

                //写数据
                using (FileStream fsWrite = new FileStream(@"C:\" + fileName, FileMode.Append, FileAccess.Write))
                {
                    fsWrite.Write(byteToSend, 0, byteToSend.Length);
                    fsWrite.Flush();
                }

                //预报当前发送状态
                long percentage = (j+1)*100/totalBatch;
                //while循环能够保证最后一批数据顺利推送到前台
                while (FileUploadWatcher.asyncResults.Count == 0 && percentage == 100)
                { }
                if (FileUploadWatcher.asyncResults.Count != 0)
                {
                    FileUploadWatcher.asyncResults[0].percent = percentage;
                    FileUploadWatcher.asyncResults[0].CompleteTask();
                }
            }
            fs.Close();
        }

        public bool IsReusable
        {
            get
            {
                return false;
            }
        }
    }
}

 

 

文件上传这块,主要是通过将大文件分割,然后放到一个容积为5000字节的缓冲区中,分段发送,以避免出现OutOfMemory的错误。所以,这种处理机制可以保证发送大数据的文件,比如说1GB大小的文件。在每次写文件的时候,程序会利用long percentage = (j+1)*100/totalBatch;来计算当前的进度,并且通过如下的代码来将当前的进度数据进行推送:

while (FileUploadWatcher.asyncResults.Count == 0 && percentage == 100)
{ }
if (FileUploadWatcher.asyncResults.Count != 0)
{
         FileUploadWatcher.asyncResults[0].percent = percentage;
         FileUploadWatcher.asyncResults[0].CompleteTask();
}
如果当前有请求,那么就可以调用异步Handler中的CompleteTask来报告本次的进度,CompleteTask将会抛出事件来触发EndProcessRequest,EndProcessRequest则会将当前的进度数据推送至前台。
While循环主要是保证最后一次数据推送能够正常完成,去掉这句,数据一直会显示完成度为99%。
 

前台设计

最后来看看前台设计吧:
-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head >
    <title>文件上传</title>
    <style type="text/css">
    body{font-size:12px;}
    #txtPath{border:none;border-bottom:1px solid black;width:300px;}
    #fileUpload{border:none;border-bottom:1px solid black;width:250px;background-color:White;height:25px;}
    #btnUpload{border:none;background:url(Image/btn_01.gif) no-repeat;width:100px;height:30px;}
    #result {width:400px;}
    </style>
    <link href="Styles/jquery-ui-1.8.16.custom.css" rel="stylesheet" type="text/css" />
    <script type="text/javascript" src="Scripts/jquery-1.6.4.min.js"></script>
    <script src="Scripts/jquery-ui-1.8.16.custom.min.js" type="text/javascript"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            $("#btnUpload").bind("click", function () {
                StartFileUpload();
                TriggerAjax();
            });
        });
        var StartFileUpload = function () {
            var filePath = $("#fileUpload").val();
            var handerUrl = "Handlers/FileUpload.ashx?path=" + filePath;
            $.ajax({
                url: handerUrl,
                type: "GET",
                success: function (result) {},
                error: function (result) {}
            });
        }

        var TriggerAjax = function () {
            var handerUrl = "Handlers/FileUploadWatcher.ashx";
            $.ajax({
                url: handerUrl,
                type: "GET",
                success: function (result) {
                    var data = parseInt(result);
                    $("#result").progressbar({ value: data });
                    $("#percent").text(data+"%");
                    if (result != 100) {
                        TriggerAjax();
                    }
                },
                error: function (result) {
                    debugger;
                    alert(result);
                }
            });
        }
    </script>
</head>
<body>
    <form  >
    请输入目标路径:<asp:TextBox ID="txtPath" runat="server" Text="\\192.168.0.180\MatiSoftDaemon\TestUploading" ></asp:TextBox>
    <br />
    <br />
    请选择本地文件:<asp:FileUpload ID="fileUpload" runat="server" />
    <input  />
    <br />
    <div  ></div>
    <div ></div>
    </form>
</body>
</html>


前台代码很简单,就是通过StartFileUpload()函数触发文件上传动作, TriggerAjax()函数触发进度检测动作。
 

源代码

 
 

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
ASP.NETMVC+EasyUI权限设计(二)环境搭建发布时间:2022-07-10
下一篇:
ASP.NET安全问题--ASP.NET安全架构发布时间:2022-07-10
热门推荐
热门话题
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap