www.pudn.com > HttpClient.rar > HttpClient.cs, change:2008-05-16,size:21049b


using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Text; 
using System.Net; 
using System.Web; 
 
namespace Utility 
{ 
    public class HttpClient 
    { 
        #region fields 
        private bool keepContext; 
        private string defaultLanguage = "zh-CN"; 
        private Encoding defaultEncoding = Encoding.UTF8; 
        private string accept = "*/*"; 
        private string userAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; 
        private HttpVerb verb = HttpVerb.GET; 
        private HttpClientContext context; 
        private readonly List files = new List(); 
        private readonly Dictionary postingData = new Dictionary(); 
        private string url; 
        private WebHeaderCollection responseHeaders; 
        private int startPoint; 
        private int endPoint; 
        #endregion 
 
        #region events 
        public event EventHandler StatusUpdate; 
 
        private void OnStatusUpdate(StatusUpdateEventArgs e) 
        { 
            EventHandler temp = StatusUpdate; 
 
            if (temp != null) 
                temp(this, e); 
        } 
        #endregion 
 
        #region properties 
        ///  
        /// 是否自动在不同的请求间保留Cookie, Referer 
        ///  
        public bool KeepContext 
        { 
            get { return keepContext; } 
            set { keepContext = value; } 
        } 
 
        ///  
        /// 期望的回应的语言 
        ///  
        public string DefaultLanguage 
        { 
            get { return defaultLanguage; } 
            set { defaultLanguage = value; } 
        } 
 
        ///  
        /// GetString()如果不能从HTTP头或Meta标签中获取编码信息,则使用此编码来获取字符串 
        ///  
        public Encoding DefaultEncoding 
        { 
            get { return defaultEncoding; } 
            set { defaultEncoding = value; } 
        } 
 
        ///  
        /// 指示发出Get请求还是Post请求 
        ///  
        public HttpVerb Verb 
        { 
            get { return verb; } 
            set { verb = value; } 
        } 
 
        ///  
        /// 要上传的文件.如果不为空则自动转为Post请求 
        ///  
        public List Files 
        { 
            get { return files; } 
        } 
 
        ///  
        /// 要发送的Form表单信息 
        ///  
        public Dictionary PostingData 
        { 
            get { return postingData; } 
        } 
 
        ///  
        /// 获取或设置请求资源的地址 
        ///  
        public string Url 
        { 
            get { return url; } 
            set { url = value; } 
        } 
 
        ///  
        /// 用于在获取回应后,暂时记录回应的HTTP头 
        ///  
        public WebHeaderCollection ResponseHeaders 
        { 
            get { return responseHeaders; } 
        } 
 
        ///  
        /// 获取或设置期望的资源类型 
        ///  
        public string Accept 
        { 
            get { return accept; } 
            set { accept = value; } 
        } 
 
        ///  
        /// 获取或设置请求中的Http头User-Agent的值 
        ///  
        public string UserAgent 
        { 
            get { return userAgent; } 
            set { userAgent = value; } 
        } 
 
        ///  
        /// 获取或设置Cookie及Referer 
        ///  
        public HttpClientContext Context 
        { 
            get { return context; } 
            set { context = value; } 
        } 
 
        ///  
        /// 获取或设置获取内容的起始点,用于断点续传,多线程下载等 
        ///  
        public int StartPoint 
        { 
            get { return startPoint; } 
            set { startPoint = value; } 
        } 
 
        ///  
        /// 获取或设置获取内容的结束点,用于断点续传,多下程下载等. 
        /// 如果为0,表示获取资源从StartPoint开始的剩余内容 
        ///  
        public int EndPoint 
        { 
            get { return endPoint; } 
            set { endPoint = value; } 
        } 
 
        #endregion 
 
        #region constructors 
        ///  
        /// 构造新的HttpClient实例 
        ///  
        public HttpClient() 
            : this(null) 
        { 
        } 
 
        ///  
        /// 构造新的HttpClient实例 
        ///  
        /// 要获取的资源的地址 
        public HttpClient(string url) 
            : this(url, null) 
        { 
        } 
 
        ///  
        /// 构造新的HttpClient实例 
        ///  
        /// 要获取的资源的地址 
        /// Cookie及Referer 
        public HttpClient(string url, HttpClientContext context) 
            : this(url, context, false) 
        { 
        } 
 
        ///  
        /// 构造新的HttpClient实例 
        ///  
        /// 要获取的资源的地址 
        /// Cookie及Referer 
        /// 是否自动在不同的请求间保留Cookie, Referer 
        public HttpClient(string url, HttpClientContext context, bool keepContext) 
        { 
            this.url = url; 
            this.context = context; 
            this.keepContext = keepContext; 
            if (this.context == null) 
                this.context = new HttpClientContext(); 
        } 
        #endregion 
 
        #region AttachFile 
        ///  
        /// 在请求中添加要上传的文件 
        ///  
        /// 要上传的文件路径 
        /// 文件字段的名称(相当于<input type=file name=fieldName>)里的fieldName) 
        public void AttachFile(string fileName, string fieldName) 
        { 
            HttpUploadingFile file = new HttpUploadingFile(fileName, fieldName); 
            files.Add(file); 
        } 
 
        ///  
        /// 在请求中添加要上传的文件 
        ///  
        /// 要上传的文件内容 
        /// 文件名 
        /// 文件字段的名称(相当于<input type=file name=fieldName>)里的fieldName) 
        public void AttachFile(byte[] data, string fileName, string fieldName) 
        { 
            HttpUploadingFile file = new HttpUploadingFile(data, fileName, fieldName); 
            files.Add(file); 
        } 
        #endregion 
 
        ///  
        /// 清空PostingData, Files, StartPoint, EndPoint, ResponseHeaders, 并把Verb设置为Get. 
        /// 在发出一个包含上述信息的请求后,必须调用此方法或手工设置相应属性以使下一次请求不会受到影响. 
        ///  
        public void Reset() 
        { 
            verb = HttpVerb.GET; 
            files.Clear(); 
            postingData.Clear(); 
            responseHeaders = null; 
            startPoint = 0; 
            endPoint = 0; 
        } 
 
        private HttpWebRequest CreateRequest() 
        { 
            HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url); 
            req.AllowAutoRedirect = false; 
            req.CookieContainer = new CookieContainer(); 
            req.Headers.Add("Accept-Language", defaultLanguage); 
            req.Accept = accept; 
            req.UserAgent = userAgent; 
            req.KeepAlive = false; 
 
            if (context.Cookies != null) 
                req.CookieContainer.Add(context.Cookies); 
            if (!string.IsNullOrEmpty(context.Referer)) 
                req.Referer = context.Referer; 
 
            if (verb == HttpVerb.HEAD) 
            { 
                req.Method = "HEAD"; 
                return req; 
            } 
 
            if (postingData.Count > 0 || files.Count > 0) 
                verb = HttpVerb.POST; 
 
            if (verb == HttpVerb.POST) 
            { 
                req.Method = "POST"; 
 
                MemoryStream memoryStream = new MemoryStream(); 
                StreamWriter writer = new StreamWriter(memoryStream); 
 
                if (files.Count > 0) 
                { 
                    string newLine = "\r\n"; 
                    string boundary = Guid.NewGuid().ToString().Replace("-", ""); 
                    req.ContentType = "multipart/form-data; boundary=" + boundary; 
 
                    foreach (string key in postingData.Keys) 
                    { 
                        writer.Write("--" + boundary + newLine); 
                        writer.Write("Content-Disposition: form-data; name=\"{0}\"{1}{1}", key, newLine); 
                        writer.Write(postingData[key] + newLine); 
                    } 
 
                    foreach (HttpUploadingFile file in files) 
                    { 
                        writer.Write("--" + boundary + newLine); 
                        writer.Write( 
                            "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"{2}", 
                            file.FieldName, 
                            file.FileName, 
                            newLine 
                            ); 
                        writer.Write("Content-Type: application/octet-stream" + newLine + newLine); 
                        writer.Flush(); 
                        memoryStream.Write(file.Data, 0, file.Data.Length); 
                        writer.Write(newLine); 
                        writer.Write("--" + boundary + newLine); 
                    } 
                } 
                else 
                { 
                    req.ContentType = "application/x-www-form-urlencoded"; 
                    StringBuilder sb = new StringBuilder(); 
                    foreach (string key in postingData.Keys) 
                    { 
                        sb.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(postingData[key])); 
                    } 
                    if (sb.Length > 0) 
                        sb.Length--; 
                    writer.Write(sb.ToString()); 
                } 
 
                writer.Flush(); 
 
                using (Stream stream = req.GetRequestStream()) 
                { 
                    memoryStream.WriteTo(stream); 
                } 
            } 
 
            if (startPoint != 0 && endPoint != 0) 
                req.AddRange(startPoint, endPoint); 
            else if (startPoint != 0 && endPoint == 0) 
                req.AddRange(startPoint); 
 
            return req; 
        } 
 
        ///  
        /// 发出一次新的请求,并返回获得的回应 
        /// 调用此方法永远不会触发StatusUpdate事件. 
        ///  
        /// 相应的HttpWebResponse 
        public HttpWebResponse GetResponse() 
        { 
            HttpWebRequest req = CreateRequest(); 
            HttpWebResponse res = (HttpWebResponse)req.GetResponse(); 
            responseHeaders = res.Headers; 
            if (keepContext) 
            { 
                context.Cookies = res.Cookies; 
                context.Referer = url; 
            } 
            return res; 
        } 
 
        ///  
        /// 发出一次新的请求,并返回回应内容的流 
        /// 调用此方法永远不会触发StatusUpdate事件. 
        ///  
        /// 包含回应主体内容的流 
        public Stream GetStream() 
        { 
            return GetResponse().GetResponseStream(); 
        } 
 
        ///  
        /// 发出一次新的请求,并以字节数组形式返回回应的内容 
        /// 调用此方法会触发StatusUpdate事件 
        ///  
        /// 包含回应主体内容的字节数组 
        public byte[] GetBytes() 
        { 
            HttpWebResponse res = GetResponse(); 
            int length = (int)res.ContentLength; 
 
            MemoryStream memoryStream = new MemoryStream(); 
            byte[] buffer = new byte[0x100]; 
            Stream rs = res.GetResponseStream(); 
            for (int i = rs.Read(buffer, 0, buffer.Length); i > 0; i = rs.Read(buffer, 0, buffer.Length)) 
            { 
                memoryStream.Write(buffer, 0, i); 
                OnStatusUpdate(new StatusUpdateEventArgs((int)memoryStream.Length, length)); 
            } 
            rs.Close(); 
 
            return memoryStream.ToArray(); 
        } 
 
        ///  
        /// 发出一次新的请求,以Http头,或Html Meta标签,或DefaultEncoding指示的编码信息对回应主体解码 
        /// 调用此方法会触发StatusUpdate事件 
        ///  
        /// 解码后的字符串 
        public string GetString() 
        { 
            byte[] data = GetBytes(); 
            string encodingName = GetEncodingFromHeaders(); 
 
            if (encodingName == null) 
                encodingName = GetEncodingFromBody(data); 
 
            Encoding encoding; 
            if (encodingName == null) 
                encoding = defaultEncoding; 
            else 
            { 
                try 
                { 
                    encoding = Encoding.GetEncoding(encodingName); 
                } 
                catch (ArgumentException) 
                { 
                    encoding = defaultEncoding; 
                } 
            } 
            return encoding.GetString(data); 
        } 
 
        ///  
        /// 发出一次新的请求,对回应的主体内容以指定的编码进行解码 
        /// 调用此方法会触发StatusUpdate事件 
        ///  
        /// 指定的编码 
        /// 解码后的字符串 
        public string GetString(Encoding encoding) 
        { 
            byte[] data = GetBytes(); 
            return encoding.GetString(data); 
        } 
 
        private string GetEncodingFromHeaders() 
        { 
            string encoding = null; 
            string contentType = responseHeaders["Content-Type"]; 
            if (contentType != null) 
            { 
                int i = contentType.IndexOf("charset="); 
                if (i != -1) 
                { 
                    encoding = contentType.Substring(i + 8); 
                } 
            } 
            return encoding; 
        } 
 
        private string GetEncodingFromBody(byte[] data) 
        { 
            string encodingName = null; 
            string dataAsAscii = Encoding.ASCII.GetString(data); 
            if (dataAsAscii != null) 
            { 
                int i = dataAsAscii.IndexOf("charset="); 
                if (i != -1) 
                { 
                    int j = dataAsAscii.IndexOf("\"", i); 
                    if (j != -1) 
                    { 
                        int k = i + 8; 
                        encodingName = dataAsAscii.Substring(k, (j - k) + 1); 
                        char[] chArray = new char[2] { '>', '"' }; 
                        encodingName = encodingName.TrimEnd(chArray); 
                    } 
                } 
            } 
            return encodingName; 
        } 
 
        ///  
        /// 发出一次新的Head请求,获取资源的长度 
        /// 此请求会忽略PostingData, Files, StartPoint, EndPoint, Verb 
        ///  
        /// 返回的资源长度 
        public int HeadContentLength() 
        { 
            Reset(); 
            HttpVerb lastVerb = verb; 
            verb = HttpVerb.HEAD; 
            using (HttpWebResponse res = GetResponse()) 
            { 
                verb = lastVerb; 
                return (int)res.ContentLength; 
            } 
        } 
 
        ///  
        /// 发出一次新的请求,把回应的主体内容保存到文件 
        /// 调用此方法会触发StatusUpdate事件 
        /// 如果指定的文件存在,它会被覆盖 
        ///  
        /// 要保存的文件路径 
        public void SaveAsFile(string fileName) 
        { 
            SaveAsFile(fileName, FileExistsAction.Overwrite); 
        } 
 
        ///  
        /// 发出一次新的请求,把回应的主体内容保存到文件 
        /// 调用此方法会触发StatusUpdate事件 
        ///  
        /// 要保存的文件路径 
        /// 指定的文件存在时的选项 
        /// 是否向目标文件写入了数据 
        public bool SaveAsFile(string fileName, FileExistsAction existsAction) 
        { 
            byte[] data = GetBytes(); 
            switch (existsAction) 
            { 
                case FileExistsAction.Overwrite: 
                    using (BinaryWriter writer = new BinaryWriter(new FileStream(fileName, FileMode.OpenOrCreate, FileAccess.Write))) 
                        writer.Write(data); 
                    return true; 
 
                case FileExistsAction.Append: 
                    using (BinaryWriter writer = new BinaryWriter(new FileStream(fileName, FileMode.Append, FileAccess.Write))) 
                        writer.Write(data); 
                    return true; 
 
                default: 
                    if (!File.Exists(fileName)) 
                    { 
                        using ( 
                            BinaryWriter writer = 
                                new BinaryWriter(new FileStream(fileName, FileMode.Create, FileAccess.Write))) 
                            writer.Write(data); 
                        return true; 
                    } 
                    else 
                    { 
                        return false; 
                    } 
            } 
        } 
    } 
 
    public class HttpClientContext 
    { 
        private CookieCollection cookies; 
        private string referer; 
 
        public CookieCollection Cookies 
        { 
            get { return cookies; } 
            set { cookies = value; } 
        } 
 
        public string Referer 
        { 
            get { return referer; } 
            set { referer = value; } 
        } 
    } 
 
    public enum HttpVerb 
    { 
        GET, 
        POST, 
        HEAD, 
    } 
 
    public enum FileExistsAction 
    { 
        Overwrite, 
        Append, 
        Cancel, 
    } 
 
    public class HttpUploadingFile 
    { 
        private string fileName; 
        private string fieldName; 
        private byte[] data; 
 
        public string FileName 
        { 
            get { return fileName; } 
            set { fileName = value; } 
        } 
 
        public string FieldName 
        { 
            get { return fieldName; } 
            set { fieldName = value; } 
        } 
 
        public byte[] Data 
        { 
            get { return data; } 
            set { data = value; } 
        } 
 
        public HttpUploadingFile(string fileName, string fieldName) 
        { 
            this.fileName = fileName; 
            this.fieldName = fieldName; 
            using (FileStream stream = new FileStream(fileName, FileMode.Open)) 
            { 
                byte[] inBytes = new byte[stream.Length]; 
                stream.Read(inBytes, 0, inBytes.Length); 
                data = inBytes; 
            } 
        } 
 
        public HttpUploadingFile(byte[] data, string fileName, string fieldName) 
        { 
            this.data = data; 
            this.fileName = fileName; 
            this.fieldName = fieldName; 
        } 
    } 
 
    public class StatusUpdateEventArgs : EventArgs 
    { 
        private readonly int bytesGot; 
        private readonly int bytesTotal; 
 
        public StatusUpdateEventArgs(int got, int total) 
        { 
            bytesGot = got; 
            bytesTotal = total; 
        } 
 
        ///  
        /// 已经下载的字节数 
        ///  
        public int BytesGot 
        { 
            get { return bytesGot; } 
        } 
 
        ///  
        /// 资源的总字节数 
        ///  
        public int BytesTotal 
        { 
            get { return bytesTotal; } 
        } 
    } 
}