如何实现一个通用的IHttpHandler 万能的IHttpHandler HttpWebRequest文件上传
昨天遇到一个比较奇怪的需求,大致是需要在服务器上部署一个http服务,但是服务的具体功能不知道,以后在客服端实现。这里介绍一下系统背景,有一个系统运(部署在美国)行了很多年了,给系统产生了很多文件,现在需要把该系统的文件(依据数据库中的记录)来做相应的archive,做了后发现里面还有一些独立的文件(不与数据库记录相关),那么这时我们需要删除这些独立的文件,或者把它们remove到其他地方,需要得到这些文件的list。后来想了想以后会不会还有别的什么需求啊,所以就想做一个通用的HTTPhandler了。这里说明一下:production时在美国,Archive在香港;在我们大陆的系统权限放的都比较开,在美国那个权限管的非常紧,我们是没有权限直接操作Production上的文件,所以才需要用http 协议来做。这里的http server部署到US,而client 却部署到hk。
整个解决方案如图:
其中
WebApp项目部署到Production上(us)
ConsoleApp部署到archive上(hk)
HttpRequestLibrary 是一个对象序列化的通用类以及一个请求类的包装,WebApp和ConsoleApp都需要引用该dll
ProcessAction是在客户端实现的,但是在服务器端反序列化是必须有该文件,所以该dll将会从client 上传到Production上。
首先我们来看看服务器端的实现:
首先需要创建一个ProcessActionHandler.ashx来处理客户端的调用:
- public class ProcessActionHandler : IHttpHandler
- {
- public void ProcessRequest(HttpContext context)
- {
- context.Response.ContentType = "text/plain";
- try
- {
- string inputstring = ReadInputStream();
- if (!string.IsNullOrEmpty(inputstring))
- {
- HttpRequestInfo requestinfo = inputstring;
- if (requestinfo.Process != null)
- {
- requestinfo.Process(requestinfo);
- }
- }
- else
- {
- //context.Response.StatusCode = 404;
- context.Response.Write("input error message");
- }
- }
- catch (Exception ex)
- {
- context.Response.Write(ex.Message);
- }
- }
- private string ReadInputStream()
- {
- StringBuilder inputString = new StringBuilder();
- using (Stream sr = HttpContext.Current.Request.InputStream)
- {
- byte[] data = new byte[ * ];
- int readCount = sr.Read(data, , data.Length);
- while (readCount > )
- {
- string text = Encoding.UTF8.GetString(data, , readCount);
- inputString.Append(text);
- readCount = sr.Read(data, , data.Length);
- }
- }
- return inputString.ToString();
- }
- public bool IsReusable
- {
- get
- {
- return false;
- }
- }
- }
这里的HttpRequestInfo类是客户端创建的,这里调用HttpRequestInfo的Process方法也是客户端实现的。如何才能获得客户端的实现了,我们需要把客户端实现的dll文件上传到服务器上。
所以需要创建一个UploadActionHandler.ashx来上传客户端的处理:
- public class UploadActionHandler : IHttpHandler
- {
- public void ProcessRequest(HttpContext context)
- {
- context.Response.ContentType = "text/plain";
- string baseFilePath = context.Server.MapPath("Bin");
- if (context.Request.Files.Count > )
- {
- try
- {
- HttpPostedFile file = context.Request.Files[];
- FileInfo fileInfo = new FileInfo(file.FileName);
- if (fileInfo.Extension.Equals(".dll"))
- {
- string tempPath = tempPath = Path.Combine(baseFilePath, fileInfo.Name);
- file.SaveAs(tempPath);
- context.Response.Write("Success");
- }
- else
- {
- context.Response.Write("Failed:\r\n There only upload dll file");
- }
- }
- catch (Exception ex)
- {
- context.Response.Write("Failed:\r\n" + ex.Message);
- }
- }
- else
- {
- context.Response.Write("Failed:\r\nThe Request has not upload file");
- }
- }
- public bool IsReusable
- {
- get
- {
- return false;
- }
- }
- }
那么对象时如何序列化和反序列化,以及HttpRequestInfo的定义是什么样的了,这就要参考我们的HttpRequestLibrary项目了。
- namespace HttpRequestLibrary
- {
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Net;
- using System.Runtime.Remoting.Messaging;
- using System.Runtime.Serialization.Formatters.Binary;
- using System.Runtime.Serialization.Formatters.Soap;
- using System.Text;
- using System.Web;
- public enum FormatterType
- {
- /// <summary>
- /// SOAP消息格式编码
- /// </summary>
- Soap,
- /// <summary>
- /// 二进制消息格式编码
- /// </summary>
- Binary
- }
- public static class SerializationHelper
- {
- private const FormatterType DefaultFormatterType = FormatterType.Binary;
- /// <summary>
- /// 按照串行化的编码要求,生成对应的编码器。
- /// </summary>
- /// <param name="formatterType"></param>
- /// <returns></returns>
- private static IRemotingFormatter GetFormatter(FormatterType formatterType)
- {
- switch (formatterType)
- {
- case FormatterType.Binary: return new BinaryFormatter();
- case FormatterType.Soap: return new SoapFormatter();
- }
- throw new NotSupportedException();
- }
- /// <summary>
- /// 把对象序列化转换为字符串
- /// </summary>
- /// <param name="graph">可串行化对象实例</param>
- /// <param name="formatterType">消息格式编码类型(Soap或Binary型)</param>
- /// <returns>串行化转化结果</returns>
- /// <remarks>调用BinaryFormatter或SoapFormatter的Serialize方法实现主要转换过程。
- /// </remarks>
- public static string SerializeObjectToString(object graph, FormatterType formatterType)
- {
- using (MemoryStream memoryStream = new MemoryStream())
- {
- IRemotingFormatter formatter = GetFormatter(formatterType);
- formatter.Serialize(memoryStream, graph);
- Byte[] arrGraph = memoryStream.ToArray();
- return Convert.ToBase64String(arrGraph);
- }
- }
- public static string SerializeObjectToString(object graph)
- {
- return SerializeObjectToString(graph, DefaultFormatterType);
- }
- /// <summary>
- /// 把已序列化为字符串类型的对象反序列化为指定的类型
- /// </summary>
- /// <param name="serializedGraph">已序列化为字符串类型的对象</param>
- /// <param name="formatterType">消息格式编码类型(Soap或Binary型)</param>
- /// <typeparam name="T">对象转换后的类型</typeparam>
- /// <returns>串行化转化结果</returns>
- /// <remarks>调用BinaryFormatter或SoapFormatter的Deserialize方法实现主要转换过程。
- /// </remarks>
- public static T DeserializeStringToObject<T>(string graph, FormatterType formatterType)
- {
- Byte[] arrGraph = Convert.FromBase64String(graph);
- using (MemoryStream memoryStream = new MemoryStream(arrGraph))
- {
- IRemotingFormatter formatter = GetFormatter(formatterType);
- return (T)formatter.Deserialize(memoryStream);
- }
- }
- public static T DeserializeStringToObject<T>(string graph)
- {
- return DeserializeStringToObject<T>(graph, DefaultFormatterType);
- }
- }
- [Serializable]
- public class HttpRequestInfo
- {
- public HttpRequestInfo()
- {
- ContentData = new byte[0];
- CommData = new Dictionary<string, string>();
- }
- public byte[] ContentData { set; get; }
- public Action<HttpRequestInfo> Process { set; get; }
- public Dictionary<string, string> CommData { set; get; }
- public override string ToString()
- {
- string graph = SerializationHelper.SerializeObjectToString(this);
- return graph;
- }
- public static implicit operator HttpRequestInfo(string contentString)
- {
- return SerializationHelper.DeserializeStringToObject<HttpRequestInfo>(contentString);
- }
- }
- }
那么客服端如何来操作服务器端了,需要查看ProcessAction项目的实现了:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Net;
- using System.IO;
- using HttpRequestLibrary;
- using System.Web;
- namespace ProcessAction
- {
- public class HttpCommProcess
- {
- public static bool UploadFile(string address, string fileNamePath, out string error)
- {
- try
- {
- error = string.Empty;
- string strBoundary = "----------" + DateTime.Now.Ticks.ToString("x");
- byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + strBoundary + "\r\n");
- StringBuilder sb = new StringBuilder();
- sb.Append("--");
- sb.Append(strBoundary);
- sb.Append("\r\n");
- sb.Append("Content-Disposition: form-data; name=\"");
- sb.Append("file");
- sb.Append("\"; filename=\"");
- sb.Append(fileNamePath);
- sb.Append("\"");
- sb.Append("\r\n");
- sb.Append("Content-Type: ");
- sb.Append(@"application\octet-stream");
- sb.Append("\r\n");
- sb.Append("\r\n");
- string strPostHeader = sb.ToString();
- byte[] postHeaderBytes = Encoding.UTF8.GetBytes(strPostHeader);
- HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(new Uri(address));
- httpReq.Method = "POST";
- httpReq.AllowWriteStreamBuffering = false;
- httpReq.Timeout = 300000;
- httpReq.ContentType = "multipart/form-data; boundary=" + strBoundary;
- string responseText = string.Empty;
- using (FileStream fs = new FileStream(fileNamePath, FileMode.Open, FileAccess.Read))
- {
- BinaryReader r = new BinaryReader(fs);
- httpReq.ContentLength = fs.Length + postHeaderBytes.Length + boundaryBytes.Length; ;
- byte[] buffer = new byte[fs.Length];
- int size = r.Read(buffer, 0, buffer.Length);
- using (Stream postStream = httpReq.GetRequestStream())
- {
- postStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);
- postStream.Write(buffer, 0, size);
- postStream.Write(boundaryBytes, 0, boundaryBytes.Length);
- }
- }
- WebResponse webRespon = httpReq.GetResponse();
- using (StreamReader s = new StreamReader(webRespon.GetResponseStream()))
- {
- responseText = s.ReadToEnd();
- }
- if (responseText.Contains("Success"))
- {
- return true;
- }
- else
- {
- error = "UploadFile :" + responseText;
- return false;
- }
- }
- catch (Exception ex)
- {
- error = "UploadFile:" + ex.Message;
- return false;
- }
- }
- public static void SendHttpRequestData( string url,string reuestContent)
- {
- try
- {
- HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
- request.Method = "POST";
- request.ContentType = "text/xml";
- request.KeepAlive = false;
- request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; .NET CLR 1.0.3705;)";
- using (Stream sr = request.GetRequestStream())
- {
- byte[] data = Encoding.UTF8.GetBytes(reuestContent);
- sr.Write(data, 0, data.Length);
- }
- HttpWebResponse response = (HttpWebResponse)request.GetResponse();
- if (response.StatusCode == HttpStatusCode.OK)
- {
- StringBuilder responseMessage = new StringBuilder();
- using (Stream sr = response.GetResponseStream())
- {
- byte[] data = new byte[1024 * 10];
- int readcount = sr.Read(data, 0, data.Length);
- while (readcount > 0)
- {
- string str = Encoding.UTF8.GetString(data, 0, readcount);
- responseMessage.Append(str);
- readcount = sr.Read(data, 0, data.Length);
- }
- Console.WriteLine(responseMessage);
- }
- }
- }
- catch (Exception ex)
- {
- Console.WriteLine(ex.Message);
- }
- }
- public static string GetUploadFileContent(string filename)
- {
- HttpRequestInfo requestInfo = new HttpRequestInfo();
- FileInfo file = new FileInfo(filename);
- requestInfo.CommData.Add("FileName", file.Name);
- requestInfo.ContentData = new byte[file.Length];
- using (Stream sr = File.OpenRead(filename))
- {
- sr.Read(requestInfo.ContentData, 0, requestInfo.ContentData.Length);
- }
- requestInfo.Process = (x) =>
- {
- try
- {
- string tempfile = Path.Combine(@"c:\test", x.CommData["FileName"]);
- using (Stream wr = File.Open(tempfile, FileMode.OpenOrCreate, FileAccess.Write))
- {
- wr.Write(x.ContentData, 0, x.ContentData.Length);
- }
- HttpContext.Current.Response.Write("Success");
- }
- catch (Exception ex)
- {
- HttpContext.Current.Response.Write(ex.Message);
- }
- };
- return requestInfo.ToString();
- }
- public static string GetFileNames(string folderpath)
- {
- HttpRequestInfo requestInfo = new HttpRequestInfo();
- requestInfo.CommData.Add("FolderPath", folderpath);
- requestInfo.Process = (x) =>
- {
- try
- {
- DirectoryInfo dir=new DirectoryInfo( x.CommData["FolderPath"]);
- foreach (FileInfo item in dir.GetFiles())
- {
- HttpContext.Current.Response.Write(item.FullName+Environment.NewLine);
- }
- HttpContext.Current.Response.Write("Success");
- }
- catch (Exception ex)
- {
- HttpContext.Current.Response.Write(ex.Message);
- }
- };
- return requestInfo.ToString();
- }
- }
- }
这里我们来看看GetFileNames方法的实现吧:
- public static string GetFileNames(string folderpath)
- {
- HttpRequestInfo requestInfo = new HttpRequestInfo();
- requestInfo.CommData.Add("FolderPath", folderpath);
- requestInfo.Process = (x) =>
- {
- try
- {
- DirectoryInfo dir=new DirectoryInfo( x.CommData["FolderPath"]);
- foreach (FileInfo item in dir.GetFiles())
- {
- HttpContext.Current.Response.Write(item.FullName+Environment.NewLine);
- }
- HttpContext.Current.Response.Write("Success");
- }
- catch (Exception ex)
- {
- HttpContext.Current.Response.Write(ex.Message);
- }
- };
- return requestInfo.ToString();
- }
- }
很显然这里的Process就是服务器端将要call的回调函数。那么这个处理很显然是在客户端,服务器端如何才能识别了,就需要把该代码上传到服务器端。
那么最终客服端该如何调用该代码了:

- static void Main(string[] args)
- {
- string error = string.Empty;
- bool uploaded = HttpCommProcess.UploadFile("http://vihk2awwwdev01/webapp/UploadActionHandler.ashx", Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ProcessAction.dll"), out error);
- if (!uploaded)
- {
- Console.WriteLine(error);
- }
- else
- {
- ///upload file
- string content = HttpCommProcess.GetUploadFileContent(@"C:\IPC.LOG");
- Console.WriteLine("Upload Fils");
- HttpCommProcess.SendHttpRequestData("http://vihk2awwwdev01/webapp/ProcessActionHandler.ashx", content);
- //get file List
- content = HttpCommProcess.GetFileNames(@"C:\ArchiveInfoCenter\ArchiveInfoCenter");
- Console.WriteLine("Get Fils List");
- HttpCommProcess.SendHttpRequestData("http://vihk2awwwdev01/webapp/ProcessActionHandler.ashx", content);
- }
- Console.ReadLine();
- }
首先上传dll文件,然后在发送http请求,运行结果如图:
客户端结果:
服务器文件上传结果(这里只能上传小文件,大文件序列化和反序列化会很慢很慢)
服务器上原文件目录:
在某种程度上我也不赞成这样做,会很危险的。这里只是纯粹从技术的角度来讲如何实现,有不好的地方还请大家拍砖。
源码下载地址:http://download.csdn.net/detail/dz45693/5856523
如何实现一个通用的IHttpHandler 万能的IHttpHandler HttpWebRequest文件上传的更多相关文章
- PHP封装一个通用好用的文件上传处理类
封装一个文件上传类完成基本功能如下: 1.可上传多个或单个文件 2.上传成功返回一个或多个文件名 3.上传失败则返回每个失败文件的错误信息 上传类中的基本功能: 1.构造参数,用户可以自定义配置参数, ...
- 06.LoT.UI 前后台通用框架分解系列之——浮夸的图片上传
LOT.UI分解系列汇总:http://www.cnblogs.com/dunitian/p/4822808.html#lotui LoT.UI开源地址如下:https://github.com/du ...
- 用Canvas+Javascript FileAPI 实现一个跨平台的图片剪切、滤镜处理、上传下载工具
直接上代码,其中上传功能需要自己配置允许跨域的文件服务器地址~ 或者将html文件贴到您的站点下同源上传也OK. 支持: 不同尺寸图片获取. 原图缩小放大. 原图移动. 选择框大小改变. 下载选中的区 ...
- [转]一个文件上传的jquery插件
http://www.jb51.net/article/51547.htm 这篇文章主要介绍了使用ajaxfileupload.js实现ajax上传文件php版,需要的朋友可以参考下 无论是P ...
- joomla安装插件报错:上传文件到服务器发生了一个错误。 过小的PHP文件上传尺寸
在安装joomla的AKeeba插件的时候报错如下:上传文件到服务器发生了一个错误. 过小的PHP文件上传尺寸.解决方法是修改php.ini文件,打开文件后搜索upload_max_filesize! ...
- 分享一个PHP文件上传类
该类用于处理文件上传,可以上传一个文件,也可以上传多个文件. 包括的成员属性有: private $path = "./uploads"; //上传文件保存的路径 private ...
- struts2一个和多个文件上传及下载
struts2的文件上传相比我们自己利用第三方jar包(commons-fileupload-1.2.1.jar commons-io-1.3.2.jar )要简单的多,当然struts2里面也是 ...
- 用VSCode开发一个asp.net core2.0+angular5项目(5): Angular5+asp.net core 2.0 web api文件上传
第一部分: http://www.cnblogs.com/cgzl/p/8478993.html 第二部分: http://www.cnblogs.com/cgzl/p/8481825.html 第三 ...
- 转:【专题十一】实现一个基于FTP协议的程序——文件上传下载器
引言: 在这个专题将为大家揭开下FTP这个协议的面纱,其实学习知识和生活中的例子都是很相通的,就拿这个专题来说,要了解FTP协议然后根据FTP协议实现一个文件下载器,就和和追MM是差不多的过程的,相信 ...
随机推荐
- android 休眠唤醒机制分析(三) — suspend
本文转自:http://blog.csdn.net/g_salamander/article/details/7988340 前面我们分析了休眠的第一个阶段即浅度休眠,现在我们继续看休眠的第二个阶段 ...
- 转:Google论文之三----MapReduce
文章来自于:http://www.cnblogs.com/geekma/p/3139823.html MapReduce:大型集群上的简单数据处理 摘要 MapReduce是一个设计模型,也是一个处理 ...
- udp丢包原因分析
1. 发送方没有进行频率控制(令牌桶算法),短时间内大量的包发送到server端,server端是单线程,先epoll wait,再process,就会造程process时丢掉server传过来的包 ...
- [转]Windows平台下安装Hadoop
1.安装JDK1.6或更高版本 官网下载JDK,安装时注意,最好不要安装到带有空格的路径名下,例如:Programe Files,否则在配置Hadoop的配置文件时会找不到JDK(按相关说法,配置文件 ...
- Windows消息编程(写的不错,有前因后果)
本文主要包括以下内容: 1.简单理解Windows的消息2.通过一个简单的Win32程序理解Windows消息3.通过几个Win32程序实例进一步深入理解Windows消息4.队列消息和非队列消息5. ...
- -_-#【Angular】自定义指令directive
AngularJS学习笔记 <!DOCTYPE html> <html ng-app="Demo"> <head> <meta chars ...
- 海美迪Q5智能机顶盒的蓝牙功能
虽然在硬件上,海美迪Q5智能机顶盒没有集成蓝牙模块,但是在软件系统上,Q5是支持蓝牙驱动的,所以它可以通过USB外接蓝牙适配器来扩展出蓝牙功能,简单来说,就是你另外买个蓝牙适配器,插到Q5上面,就能用 ...
- Robot Framework安装配置 Linux
Simple introduction Robot Framework is a generic test automation framework for acceptance testing an ...
- 一个在字符串中查找多个关键字的函数strstrs(三种不同算法实现及效率分析)
平时项目中有时需要用到在字符串中搜索两个或更多的关键字的情景.例如:将字符串"ab|cd#ef|"按竖线或者井号做分隔 如果是大项目,一般会采用正则表达式做处理.但有时写个小程序, ...
- 利用Jenkins自动部署工具间接构建kettle的调度平台
关于Jenkins的介绍我就不说了,自己百度,因为这个工具调用脚本只是他的功能的冰山一角,其他功能我也不能理解,因为不是那个领域. 下面我就介绍一下为什么我们需要一个调度平台,以及学习完 ...