基于IHttpAsyncHandler的实时大文件传送器

2 篇文章 0 订阅
2 篇文章 0 订阅

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

图示结果

QQ截图20131106231102

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

QQ截图20131106231112

(图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%。
 

前台设计

最后来看看前台设计吧:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="AsyncThermometerDeamon.Default" %>

<!DOCTYPE html PUBLIC "-//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 id="Head1" runat="server">
    <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 id="form1" runat="server" >
    请输入目标路径:<asp:TextBox ID="txtPath" runat="server" Text="\\192.168.0.180\MatiSoftDaemon\TestUploading" ></asp:TextBox>
    <br />
    <br />
    请选择本地文件:<asp:FileUpload ID="fileUpload" runat="server" />
    <input id="btnUpload" type="button" value="上传文件" />
    <br />
    <div id="result" ></div>
    <div id="percent"></div>
    </form>
</body>
</html>


前台代码很简单,就是通过StartFileUpload()函数触发文件上传动作, TriggerAjax()函数触发进度检测动作。
C#网页版+客户端版聊天软件源码分享(C#+长连接+Sqllite数据库实现) 今天我给大家分享一个聊天程序的源码。 网页版加客户端版并存,可以互通 我相信对大家学习和扩展这一块知识是很有用的。 我们先来看下软件结构 一个Web版一个网页版,而客户端是连接的网页的 http://localhost:53947/wwwroot/Lesktop 这个路径 http://localhost:53947/wwwroot/这一部分是网页的地址,大家可以根据自己配置情况进行修改 然后浏览一下Default.aspx页面如下 这是负面版的,客户端的也是一样的,我们先来注册 一个账户 在这里我们注册两个账户还有一个是text用来聊天对话使用 注册的方法是一样的我就不多说了。 下面登录第一个账户看看 这是登录后的效果。 单击聊天室 然后我们再登录另外一个账户text 好了大家应该能看到效果了吧。 然后咱们再发个“你好” 收到了吧,再回复一个 对就是这个效果, 再来看看桌面版的 刚才的消息都在 这是桌面版的效果。 大家感觉怎么样。 我感觉大家可以在这个基础之上进行扩展,最少可以看看他的实现思路 源码分享给大家了 sufeinet.com即时通信_云骞.zip (3.25 MB, 下载次数: 1078) ReceiveResponsesHandler 类,这个主要是用来接收和维护长连接的 实现长连接的两个重要来代码预览 [C#] 纯文本查看 复制代码 ? 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 using System; using System.Collections.Generic; using System.Collections; using System.Text; using System.Web; using System.Xml; using System.Threading; namespace Core { public class ReceiveResponsesHandler : IHttpAsyncHandler { public ReceiveResponsesHandler() { } HttpContext m_Context = null; IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData) { m_Context = context; string sessionId = context.Request.Params["SessionID"]; string clientVersion = context.Request.Params["ClientVersion"]; string serverVersion = context.Request.Params["ServerVersion"]; ResponsesListener asyncResult = new ResponsesListener(sessionId, cb, extraData); try { if (serverVersion != ServerImpl.Instance.Version) throw new IncompatibleException();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值