昨天遇到一个比较奇怪的需求,大致是需要在服务器上部署一个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文件上传的更多相关文章

  1. PHP封装一个通用好用的文件上传处理类

    封装一个文件上传类完成基本功能如下: 1.可上传多个或单个文件 2.上传成功返回一个或多个文件名 3.上传失败则返回每个失败文件的错误信息 上传类中的基本功能: 1.构造参数,用户可以自定义配置参数, ...

  2. 06.LoT.UI 前后台通用框架分解系列之——浮夸的图片上传

    LOT.UI分解系列汇总:http://www.cnblogs.com/dunitian/p/4822808.html#lotui LoT.UI开源地址如下:https://github.com/du ...

  3. 用Canvas+Javascript FileAPI 实现一个跨平台的图片剪切、滤镜处理、上传下载工具

    直接上代码,其中上传功能需要自己配置允许跨域的文件服务器地址~ 或者将html文件贴到您的站点下同源上传也OK. 支持: 不同尺寸图片获取. 原图缩小放大. 原图移动. 选择框大小改变. 下载选中的区 ...

  4. [转]一个文件上传的jquery插件

    http://www.jb51.net/article/51547.htm 这篇文章主要介绍了使用ajaxfileupload.js实现ajax上传文件php版,需要的朋友可以参考下     无论是P ...

  5. joomla安装插件报错:上传文件到服务器发生了一个错误。 过小的PHP文件上传尺寸

    在安装joomla的AKeeba插件的时候报错如下:上传文件到服务器发生了一个错误. 过小的PHP文件上传尺寸.解决方法是修改php.ini文件,打开文件后搜索upload_max_filesize! ...

  6. 分享一个PHP文件上传类

    该类用于处理文件上传,可以上传一个文件,也可以上传多个文件. 包括的成员属性有: private $path = "./uploads"; //上传文件保存的路径 private ...

  7. struts2一个和多个文件上传及下载

    struts2的文件上传相比我们自己利用第三方jar包(commons-fileupload-1.2.1.jar   commons-io-1.3.2.jar )要简单的多,当然struts2里面也是 ...

  8. 用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 第三 ...

  9. 转:【专题十一】实现一个基于FTP协议的程序——文件上传下载器

    引言: 在这个专题将为大家揭开下FTP这个协议的面纱,其实学习知识和生活中的例子都是很相通的,就拿这个专题来说,要了解FTP协议然后根据FTP协议实现一个文件下载器,就和和追MM是差不多的过程的,相信 ...

随机推荐

  1. 转:Zend Framework 2.0 分析

    文章来自于:http://bbs.phpchina.com/thread-268362-1-1.html ZF2已经发布,与ZF1相比,MVC这一模块内部的实现机制可谓大相径庭,许多用过ZF1的PHP ...

  2. 转:Redis作者谈Redis应用场景

    毫无疑问,Redis开创了一种新的数据存储思路,使用Redis,我们不用在面对功能单调的数据库时,把精力放在如何把大象放进冰箱这样的问题上,而是利用Redis灵活多变的数据结构和数据操作,为不同的大象 ...

  3. 14.2 InnoDB and the ACID Model

    14.2 InnoDB and the ACID Model ACID 模型是一组数据库设计原则,强调可靠性方面对于商业数据和关键人物. MySQL 包含组件比如InnoDB存储引擎坚持ACID 模型 ...

  4. 199bit总结的影响最大的十个算法

    1. 归并排序(MERGE SORT).快速排序(QUICK SORT)和堆积排序(HEAP SORT) 哪个排序算法效率最高?这要看情况.这也就是我把3种算法放在一起讲的原因,可能你更常用其中一种, ...

  5. 【转】(DT系列一)DTS结构及其编译方法----不错

    原文网址:http://www.cnblogs.com/biglucky/p/4057476.html DTS结构及其编译方法 一:主要问题 1,需要了解dtsi与dts的关系 2,dts的结构模型 ...

  6. 【转】Android 二维码 生成和识别(附Demo源码)--不错

    原文网址:http://www.cnblogs.com/mythou/p/3280023.html 今天讲一下目前移动领域很常用的技术——二维码.现在大街小巷.各大网站都有二维码的踪迹,不管是IOS. ...

  7. 两台windows服务器----SVN的迁移

    两台服务器,进行SVN的迁移:系统平台:windows server 2003 版本库:test源服务器:192.168.1.14目标服务器:192.168.1.12源SVN版本库的path: D:\ ...

  8. DateTime用法

    //今天 DateTime.Now.Date.ToShortDateString(); //昨天,也就是今天的日期减一 DateTime.Now.AddDays(-1).ToShortDateStri ...

  9. 安装VS2012 update3提示缺少Microsoft根证书颁发机构2010或2011的解决方法

    警告提示如图: (copy的百度贴吧的童鞋的截图) 解决方法: 下载2010.10或2011.10的根证书即可 直通车:http://maxsky.ys168.com/ ——05.||浮云文件||—— ...

  10. redis常见错误

    1.Redis Error --MISCONF Redis is configured to save RDB snapshots省略 分析:(linux)未用root启动,用的app用户(没有最高权 ...