如何實現一個通用的IHttpHandler 萬能的IHttpHandler HttpWebRequest文件上傳

昨天遇到一個比較奇怪的需求,大體是須要在服務器上部署一個http服務,可是服務的具體功能不知道,之後在客服端實現。這裏介紹一下系統背景,有一個系統運(部署在美國)行了不少年了,給系統產生了不少文件,如今須要把該系統的文件(依據數據庫中的記錄)來作相應的archive,作了後發現裏面還有一些獨立的文件(不與數據庫記錄相關),那麼這時咱們須要刪除這些獨立的文件,或者把它們remove到其餘地方,須要獲得這些文件的list。後來想了想之後會不會還有別的什麼需求啊,因此就想作一個通用的HTTPhandler了。這裏說明一下:production時在美國,Archive在香港;在咱們大陸的系統權限放的都比較開,在美國那個權限管的很是緊,咱們是沒有權限直接操做Production上的文件,因此才須要用http 協議來作。這裏的http server部署到US,而client 卻部署到hk。web

整個解決方案如圖:數據庫

其中服務器

WebApp項目部署到Production上(us)app

ConsoleApp部署到archive上(hk)webapp

HttpRequestLibrary 是一個對象序列化的通用類以及一個請求類的包裝,WebApp和ConsoleApp都須要引用該dllide

ProcessAction是在客戶端實現的,可是在服務器端反序列化是必須有該文件,因此該dll將會從client 上傳到Production上。函數

首先咱們來看看服務器端的實現:post

首先須要建立一個ProcessActionHandler.ashx來處理客戶端的調用:ui

 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文件上傳到服務器上。this

因此須要建立一個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:\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);
        }
    }
}
View Code

那麼客服端如何來操做服務器端了,須要查看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();
        }
    }
}
View Code

這裏咱們來看看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

相關文章
相關標籤/搜索