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

时间:2023-02-22 12:55:51

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

图示结果

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

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

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

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

前台设计

最后来看看前台设计吧:
基于IHttpAsyncHandler的实时大文件传送器基于IHttpAsyncHandler的实时大文件传送器
<%@ 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()函数触发进度检测动作。
 

源代码

 
 

基于IHttpAsyncHandler的实时大文件传送器的更多相关文章

  1. 基于RMI服务传输大文件的完整解决方案

    基于RMI服务传输大文件,分为上传和下载两种操作,需要注意的技术点主要有三方面,第一,RMI服务中传输的数据必须是可序列化的.第二,在传输大文件的过程中应该有进度提醒机制,对于大文件传输来说,这点很重 ...

  2. 基于TCP协议的大文件传输&lpar;粘包问题处理&rpar;

    基于TCP的大文件上传服务端实现 # 服务端 # -*- coding: utf-8 -*- from socket import * import json, struct server = soc ...

  3. 转:wcf大文件传输解决之道(1)

    首先声明,文章思路源于MSDN中徐长龙老师的课程整理,加上自己的一些心得体会,先总结如下: 在应对与大文件传输的情况下,因为wcf默认采用的是缓存加载对象,也就是说将文件包一次性接受至缓存中,然后生成 ...

  4. Asp&period;Net上传大文件带进度条swfupload

    Asp.Net基于swfupload上传大文件带进度条百分比显示,漂亮大气上档次,大文件无压力,先看效果 一.上传效果图 1.上传前界面:图片不喜欢可以自己换 2.上传中界面:百分比显示 3.上传后返 ...

  5. ASP&period;NET CORE使用WebUploader对大文件分片上传,并通过ASP&period;NET CORE SignalR实时反馈后台处理进度给前端展示

    本次,我们来实现一个单个大文件上传,并且把后台对上传文件的处理进度通过ASP.NET CORE SignalR反馈给前端展示,比如上传一个大的zip压缩包文件,后台进行解压缩,并且对压缩包中的文件进行 ...

  6. 基于WCF的支持跨局域网可断点续传的大文件传输服务实现

    题外话:这个系列的文章记录了本人最近写的一个小工程,主要包含了两个功能,一是对文件的断点续传的功能,二是基于WCF的一对多文件主动发送的功能,顺便这也是我自己在WCF学习路上的一个小成果吧. 在网上找 ...

  7. BareTail大文件日志实时查看工具

    BareTail 动态的查看日志文件,就像Linux上的tail tail -f nohup.out 功能: 实时文件查看 tail命令模式,自动滚动 支持2g以上大文件 自动滚动 彩色监控 多文件监 ...

  8. 基于socket实现大文件上传

    import socket 1.客户端: 操作流程: 先拿到文件--->获取文件大小---->创建字典 1.制作表头 header  如何得到 他是一个二进制字符串 序列化得到 字典字符串 ...

  9. 基于 WebSocket 的聊天和大文件上传(有进度提示)完美实现

    大家好,好久没有写文章了,当然不是不想写,主要是工作太忙,公司有没有网络环境,不让上网,所以写的就少了.今天是2019年的最后一天,明天就要开始新的一年,当然也希望自己有一个新的开始.在2019年的最 ...

随机推荐

  1. storm源码之一个class解决nimbus单点问题【转】

    本文导读: storm nimbus 单节点问题概述 storm与解决nimbus单点相关的概念 nimbus目前无法做到多节点的原因 解决nimbus单点问题的关键 业界对nimbus单点问题的努力 ...

  2. Kafka笔记--参数说明及Demo

    参考资料:http://blog.csdn.net/honglei915/article/details/37563647参数说明:http://ju.outofmemory.cn/entry/119 ...

  3. cdoj 邱老师看电影

    //第一次写概率dp //写成记忆化搜索的形式比递推要更方便易懂 //不过好像还是可以写成递推的形式的 但是比较那个…… #include<cstdio> #include<iost ...

  4. 计算机网络之局域网&amp&semi;以太网

    局域网的拓扑结构 局域网最主要的特点是:网络为一个单位所拥有,且地理范围和站点数目均有限. 局域网具有广播功能,从一个站点可很方便地访问全网,局域网上的主机可共享连接在局域网上的各种硬件和软件资源. ...

  5. java Domj4读取xml文件加强训练案例

    需求:给出一段xml文件.要求按照鸳鸯输出. xml文件代码如下: <?xml version="1.0" encoding="utf-8"?> & ...

  6. 陕西柴油机--机械ip--------》QQ请求汇创

    我们发现 String.substring()所返回的 String 仍然会保存原始 String,其实substring中生成的字符串与原字符串共享内容数组是一个很棒的设计,这样避免了每次进行sub ...

  7. Spark操作实战

    1. local模式 $SPARK_HOME/bin/spark-shell --master local import org.apache.log4j.{Level,Logger} // 导入ja ...

  8. 怎样用纯HTML和CSS更改默认的上传文件按钮样式

    如果你曾经试过,你就会知道,用纯CSS样式加HTML实现统一的上传文件按钮可能会很麻烦.看看下面的不同浏览器的截图.很明显的,他们长得很不一样. 我们的目标是创造一个简洁,用纯CSS实现的,在所有浏览 ...

  9. 小程序踩过的一个小坑---解析二维码decodeURIComponent&lpar;&rpar; url解码

    因为我们需要用户扫码进入小程序,每一个货柜都有一个对应的二维码,当然每个二维码里的信息也不一样.用户扫码进入小程序之后,二维码的信息会以参数q带进去,而我们只能在onLoad事件中拿到这个参数, 但是 ...

  10. phalcon遇到的那些坑

    1.数据重复插入 数据被重复插入,一般是在index/index方法中进行数据库insert操作,会发现一条数据被重复插了一次. 原因:浏览器有时候会自动请求 /favicon.ico ,而你的网站并 ...