• 如何实现一个通用的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[1024 * 100];
                    int readCount = sr.Read(data, 0, data.Length);
                    while (readCount > 0)
                    {
                        string text = Encoding.UTF8.GetString(data, 0, readCount);
                        inputString.Append(text);
                        readCount = sr.Read(data, 0, 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 > 0)
                {
                    try
                    {
                        HttpPostedFile file = context.Request.Files[0];
                        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:
     There only upload dll file");
                        }
                    }
                    catch (Exception ex)
                    {
                        context.Response.Write("Failed:
    " + ex.Message);
                    }
                }
                else
                {
                    context.Response.Write("Failed:
    The  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("
    --" + strBoundary + "
    ");
    
                    StringBuilder sb = new StringBuilder();
                    sb.Append("--");
                    sb.Append(strBoundary);
                    sb.Append("
    ");
                    sb.Append("Content-Disposition: form-data; name="");
                    sb.Append("file");
                    sb.Append(""; filename="");
                    sb.Append(fileNamePath);
                    sb.Append(""");
                    sb.Append("
    ");
                    sb.Append("Content-Type: ");
                    sb.Append(@"applicationoctet-stream");
                    sb.Append("
    ");
                    sb.Append("
    ");
                    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:	est", 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:ArchiveInfoCenterArchiveInfoCenter");
                    Console.WriteLine("Get Fils List");
                    HttpCommProcess.SendHttpRequestData("http://vihk2awwwdev01/webapp/ProcessActionHandler.ashx", content);
                }
                Console.ReadLine();
            }

    首先上传dll文件,然后在发送http请求,运行结果如图:

    客户端结果:

    服务器文件上传结果(这里只能上传小文件,大文件序列化和反序列化会很慢很慢)

    服务器上原文件目录:

    在某种程度上我也不赞成这样做,会很危险的。这里只是纯粹从技术的角度来讲如何实现,有不好的地方还请大家拍砖。

    源码下载地址:http://download.csdn.net/detail/dz45693/5856523


  • 相关阅读:
    看动画学算法之:栈stack
    asp.net core使用identity+jwt保护你的webapi(三)——refresh token
    asp.net core使用identity+jwt保护你的webapi(二)——获取jwt token
    与 Python 之父聊天:更快的 Python!
    Oracle使用SPM对含有绑定变量SQL做固定的方法
    Oracle Hint no_merge(merge)、no_unnest(unnest)的作用对象陷阱
    Oracle全表扫描导致的direct path read第一次慢第二次以后变快
    Oracle OEM13C添加主机监控遇到一些问题汇总
    还傻傻分不清楚equals和==的区别吗?看完就明白了
    从0到1实现一个简单计算器
  • 原文地址:https://www.cnblogs.com/aukle/p/3233643.html
Copyright © 2020-2023  润新知