基于IHttpAsyncHandler的实时大文件传送器
在日常工作中,有时候需要到远程服务器上部署新版本的系统,由于远程服务器出于外网,所以每次都要开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%。
前台设计
最后来看看前台设计吧:


<%@ 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>
源代码
基于IHttpAsyncHandler的实时大文件传送器的更多相关文章
- 基于RMI服务传输大文件的完整解决方案
基于RMI服务传输大文件,分为上传和下载两种操作,需要注意的技术点主要有三方面,第一,RMI服务中传输的数据必须是可序列化的.第二,在传输大文件的过程中应该有进度提醒机制,对于大文件传输来说,这点很重 ...
- 基于TCP协议的大文件传输(粘包问题处理)
基于TCP的大文件上传服务端实现 # 服务端 # -*- coding: utf-8 -*- from socket import * import json, struct server = soc ...
- 转:wcf大文件传输解决之道(1)
首先声明,文章思路源于MSDN中徐长龙老师的课程整理,加上自己的一些心得体会,先总结如下: 在应对与大文件传输的情况下,因为wcf默认采用的是缓存加载对象,也就是说将文件包一次性接受至缓存中,然后生成 ...
- Asp.Net上传大文件带进度条swfupload
Asp.Net基于swfupload上传大文件带进度条百分比显示,漂亮大气上档次,大文件无压力,先看效果 一.上传效果图 1.上传前界面:图片不喜欢可以自己换 2.上传中界面:百分比显示 3.上传后返 ...
- ASP.NET CORE使用WebUploader对大文件分片上传,并通过ASP.NET CORE SignalR实时反馈后台处理进度给前端展示
本次,我们来实现一个单个大文件上传,并且把后台对上传文件的处理进度通过ASP.NET CORE SignalR反馈给前端展示,比如上传一个大的zip压缩包文件,后台进行解压缩,并且对压缩包中的文件进行 ...
- 基于WCF的支持跨局域网可断点续传的大文件传输服务实现
题外话:这个系列的文章记录了本人最近写的一个小工程,主要包含了两个功能,一是对文件的断点续传的功能,二是基于WCF的一对多文件主动发送的功能,顺便这也是我自己在WCF学习路上的一个小成果吧. 在网上找 ...
- BareTail大文件日志实时查看工具
BareTail 动态的查看日志文件,就像Linux上的tail tail -f nohup.out 功能: 实时文件查看 tail命令模式,自动滚动 支持2g以上大文件 自动滚动 彩色监控 多文件监 ...
- 基于socket实现大文件上传
import socket 1.客户端: 操作流程: 先拿到文件--->获取文件大小---->创建字典 1.制作表头 header 如何得到 他是一个二进制字符串 序列化得到 字典字符串 ...
- 基于 WebSocket 的聊天和大文件上传(有进度提示)完美实现
大家好,好久没有写文章了,当然不是不想写,主要是工作太忙,公司有没有网络环境,不让上网,所以写的就少了.今天是2019年的最后一天,明天就要开始新的一年,当然也希望自己有一个新的开始.在2019年的最 ...
随机推荐
- 【原/转】iOS中非常强大的过滤器:NSPredicate
在APPLE的官方Demo:UICatalog中实现UISearchBar模糊搜索功能是这么做的: - (void)viewDidLoad { [super viewDidLoad]; self.al ...
- RunTime(运行时机制)
1>runtime实现的机制是什么,怎么用,一般用于干嘛? 这个问题我就不跟大家绕弯子了,直接告诉大家, runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语 ...
- [Nginx][HttpUpstreamModule]翻译负载均衡
英文原文地址:http://nginx.org/en/docs/http/ngx_http_upstream_module.html 大纲: 示例 指令 嵌入变量 ngx_http_upstream_ ...
- 深入理解java虚拟机(6)---内存模型与线程 & Volatile
其实关于线程的使用,之前已经写过博客讲解过这部分的内容: http://www.cnblogs.com/deman/category/621531.html JVM里面关于多线程的部分,主要是多线程是 ...
- android中的坐标系以及获取坐标的方法
android中有两种坐标系,分别称之为Android坐标系和视图坐标系.而对应的也有一些相关的方法可以获取坐标系中的 坐标值.只有搞清楚这些区别,才能在实现的时候不至于出错或者得不到你想要的效果. ...
- Servlet/JSP-01 Servlet及其生命周期
一.起步 1.新建一个类继承Servlet接口 public class HelloServlet implements Servlet { @Override public void destroy ...
- java 接收 char字符型
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.Scanner; public clas ...
- hibernate数据库连接池
访问数据库,需要不断的创建和释放连接,假如访问量大的话,效率比较低级,服务器消耗大: 使用数据库连接池,我们可以根据实际项目的情况,定义连接池的连接个数,从而可以实现从连接池获取连接,用户放回到连接池 ...
- Vrrp协议
一.简介 VRRP(Virtual Router Redundancy Protocol)协议是用于实现路由器冗余的协议,最新协议在RFC3768中定义,原来的定义RFC2338被废除,新协议相对还简 ...
- POJ3648 A Simple Problem with Integers(线段树之成段更新。入门题)
A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 53169 Acc ...