工作中長期需要用到通過HTTP調用API以及文件上傳下載,積累了不少經驗,現在將各種不同方式進行一個彙總。 首先是HttpWebRequest: 然後是HttpWebResponse: 上面兩個方法需要配合使用,皆為同步方式。當然也可以將上面兩個方法合併到一個方法中,可以參見接下來這個方法。 另外是 ...
工作中長期需要用到通過HTTP調用API以及文件上傳下載,積累了不少經驗,現在將各種不同方式進行一個彙總。
首先是HttpWebRequest:
1 /// <summary> 2 /// 向伺服器發送Request 3 /// </summary> 4 /// <param name="url">字元串</param> 5 /// <param name="method">枚舉類型的方法Get或者Post</param> 6 /// <param name="body">Post時必須傳值</param> 7 /// <param name="timeoutSeconds">超時時間,單位秒</param> 8 /// <returns></returns> 9 public static string Request(string url, MethodEnum method, string body = "", int timeoutSeconds = 15000) 10 { 11 if (!IsConnectedInternet()) 12 return "網路連接錯誤,請稍後再試。"; 13 14 try 15 { 16 GC.Collect(); 17 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); 18 request.Timeout = timeoutSeconds; 19 request.Method = method.ToString(); 20 21 //如果是Post的話,則設置body 22 if (method == MethodEnum.POST) 23 { 24 request.ContentType = "application/json"; 25 request.KeepAlive = false; 26 byte[] requestBody = Encoding.UTF8.GetBytes(body); 27 request.ContentLength = requestBody.Length; 28 29 Stream requestStream = request.GetRequestStream(); 30 requestStream.Write(requestBody, 0, requestBody.Length); 31 } 32 33 return Response(request); 34 } 35 catch (Exception ex) 36 { 37 if (ex.InnerException != null) 38 return ex.InnerException.Message; 39 if (ex.Message.Contains("已取消一個任務")) 40 return "連接伺服器超時,請重試"; 41 if (ex.Message.Contains("404")) 42 return "連接伺服器404,請重試"; 43 return ex.Message; 44 } 45 }
然後是HttpWebResponse:
1 /// <summary> 2 /// 返回Response數據 3 /// </summary> 4 /// <param name="request"></param> 5 /// <returns></returns> 6 private static string Response(HttpWebRequest request) 7 { 8 HttpWebResponse response = (HttpWebResponse)request.GetResponse(); 9 10 string jsonRead = ""; 11 12 if (response.StatusCode != HttpStatusCode.OK) 13 { 14 return response.StatusCode.ToString(); 15 } 16 //接收過程 17 if (response.GetResponseStream() != null) 18 { 19 StreamReader myStreamReader = new StreamReader(response.GetResponseStream() ?? Stream.Null, Encoding.UTF8); 20 jsonRead = myStreamReader.ReadToEnd(); 21 myStreamReader.Close(); 22 } 23 response.Close(); 24 request.Abort(); 25 26 return jsonRead; 27 }
上面兩個方法需要配合使用,皆為同步方式。當然也可以將上面兩個方法合併到一個方法中,可以參見接下來這個方法。
另外是使用HttpWebRequest和HttpWebResponse進行文件上傳,採用非同步方式:
1 public static void UploadFile(string url, string filePath, string fileName, Action<string> callback) 2 { 3 // 時間戳,用做boundary 4 string timeStamp = DateTime.Now.Ticks.ToString("x"); 5 6 //根據uri創建HttpWebRequest對象 7 HttpWebRequest httpReq = (HttpWebRequest)WebRequest.Create(new Uri(url)); 8 httpReq.Method = "POST"; 9 httpReq.AllowWriteStreamBuffering = false; //對發送的數據不使用緩存 10 httpReq.Timeout = 300000; //設置獲得響應的超時時間(300秒) 11 httpReq.ContentType = "multipart/form-data; boundary=" + timeStamp; 12 13 //文件 14 FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read); 15 BinaryReader binaryReader = new BinaryReader(fileStream); 16 17 //頭信息 18 string boundary = "--" + timeStamp; 19 string dataFormat = boundary + "\r\nContent-Disposition: form-data; name=\"{0}\";filename=\"{1}\"\r\nContent-Type:application/octet-stream\r\n\r\n"; 20 string header = string.Format(dataFormat, "file", Path.GetFileName(filePath)); 21 byte[] postHeaderBytes = Encoding.UTF8.GetBytes(header); 22 23 //結束邊界 24 byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + timeStamp + "--\r\n"); 25 26 long length = fileStream.Length + postHeaderBytes.Length + boundaryBytes.Length; 27 28 httpReq.ContentLength = length;//請求內容長度 29 30 try 31 { 32 //每次上傳4k 33 int bufferLength = 4096; 34 byte[] buffer = new byte[bufferLength]; 35 36 //已上傳的位元組數 37 long offset = 0; 38 int size = binaryReader.Read(buffer, 0, bufferLength); 39 Stream postStream = httpReq.GetRequestStream(); 40 41 //發送請求頭部消息 42 postStream.Write(postHeaderBytes, 0, postHeaderBytes.Length); 43 44 while (size > 0) 45 { 46 postStream.Write(buffer, 0, size); 47 offset += size; 48 size = binaryReader.Read(buffer, 0, bufferLength); 49 } 50 51 //添加尾部邊界 52 postStream.Write(boundaryBytes, 0, boundaryBytes.Length); 53 postStream.Close(); 54 55 string returnValue = ""; 56 //獲取伺服器端的響應 57 using (HttpWebResponse response = (HttpWebResponse)httpReq.GetResponse()) 58 { 59 Stream receiveStream = response.GetResponseStream(); 60 StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8); 61 returnValue = readStream.ReadToEnd(); 62 response.Close(); 63 readStream.Close(); 64 } 65 66 callback?.Invoke(returnValue); 67 } 68 catch (Exception) 69 { 70 callback?.Invoke(""); 71 } 72 finally 73 { 74 fileStream.Close(); 75 binaryReader.Close(); 76 } 77 }
上面還用到一個enum叫MethodEnum,包含GET和POST兩個枚舉值。
還有一個比較特殊的POST方法:
1 public static string HttpPostFormData(string url, Dictionary<string, string> dic) 2 { 3 try 4 { 5 GC.Collect(); 6 // 時間戳,用做boundary 7 string timeStamp = DateTime.Now.Ticks.ToString("x"); 8 string boundary = "----" + timeStamp; 9 10 HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); 11 request.Method = WebRequestMethods.Http.Post; 12 request.ContentType = "multipart/form-data; boundary=" + boundary; 13 request.KeepAlive = true; 14 request.Timeout = 3000; 15 16 var stream = new MemoryStream(); 17 18 //頭信息 19 string dataFormat = "--" + boundary + "\r\nContent-Disposition: form-data; name=\"{0}\"\r\n\r\n{1}\r\n"; 20 foreach (string key in dic.Keys) 21 { 22 string s = string.Format(dataFormat, key, dic[key]); 23 byte[] data = Encoding.UTF8.GetBytes(s); 24 stream.Write(data, 0, data.Length); 25 } 26 27 //結束邊界 28 byte[] boundaryBytes = Encoding.ASCII.GetBytes("--" + boundary + "--"); 29 stream.Write(boundaryBytes, 0, boundaryBytes.Length); 30 31 request.ContentLength = stream.Length;//請求內容長度 32 33 Stream requestStream = request.GetRequestStream(); //寫入請求數據 34 stream.Position = 0L; 35 stream.CopyTo(requestStream); 36 stream.Close(); 37 38 requestStream.Close(); 39 40 return Response(request); 41 } 42 catch (Exception e) 43 { 44 return e.Message; 45 } 46 }
--------------------------------------------------------------------------------------我是分割線-----------------------------------------------------------------------------------------------------------
然後是HttpClient,這個類提供的都是非同步方法,下麵包含了POST、GET、PUT、DELETE四個方法,還有一個SEND方法稍加改動即可實現:
1 public static async void AsyncPost(string url, string body, Action<RequestResult> callback, int timeoutSeconds = 10) 2 { 3 var requestResult = new RequestResult(); 4 if (!IsConnectedInternet()) 5 { 6 requestResult.Message = "網路連接錯誤,請稍後再試。"; 7 callback?.Invoke(requestResult); 8 return; 9 } 10 11 try 12 { 13 using (var client = new HttpClient()) 14 { 15 client.DefaultRequestHeaders.Add("authorization", "LJQfL1A2oeP2fuEiOHo6"); 16 client.Timeout = new TimeSpan(0, 0, timeoutSeconds); 17 //byte[] requestBody = Encoding.UTF8.GetBytes(body); 18 HttpContent content = new StringContent(body); 19 content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); 20 var response = await client.PostAsync(url, content); 21 //確保HTTP成功狀態值 22 response.EnsureSuccessStatusCode(); 23 //await非同步讀取最後的JSON 24 await response.Content.ReadAsStringAsync().ContinueWith(t => 25 { 26 if (t.IsCompleted) 27 { 28 requestResult.IsSuccess = true; 29 requestResult.Result = t.Result; 30 callback?.Invoke(requestResult); 31 } 32 }); 33 } 34 } 35 catch (Exception e) 36 { 37 if (e.InnerException != null) 38 requestResult.Message = e.InnerException.Message; 39 else if (e.Message.Contains("已取消一個任務")) 40 requestResult.Message = "連接伺服器超時,請重試"; 41 else if (e.Message.Contains("404")) 42 requestResult.Message = "連接伺服器404,請重試"; 43 else 44 requestResult.Message = e.Message; 45 callback?.Invoke(requestResult); 46 } 47 } 48 49 public static async void AsyncGet(string url, Action<RequestResult> callback, int timeoutSeconds = 10) 50 { 51 var requestResult = new RequestResult(); 52 if (!IsConnectedInternet()) 53 { 54 requestResult.Message = "網路連接錯誤,請稍後再試。"; 55 callback?.Invoke(requestResult); 56 return; 57 } 58 59 try 60 { 61 using (var client = new HttpClient()) 62 { 63 client.DefaultRequestHeaders.Add("authorization", "LJQfL1A2oeP2fuEiOHo6"); 64 client.Timeout = new TimeSpan(0, 0, timeoutSeconds); 65 var response = await client.GetAsync(url); 66 //確保HTTP成功狀態值 67 response.EnsureSuccessStatusCode(); 68 //await非同步讀取最後的JSON 69 await response.Content.ReadAsStringAsync().ContinueWith(t => 70 { 71 if (t.IsCompleted) 72 { 73 requestResult.IsSuccess = true; 74 requestResult.Result = t.Result; 75 callback?.Invoke(requestResult); 76 } 77 }); 78 } 79 } 80 catch (Exception e) 81 { 82 if (e.InnerException != null) 83 requestResult.Message = e.InnerException.Message; 84 else if (e.Message.Contains("已取消一個任務")) 85 requestResult.Message = "連接伺服器超時,請重試"; 86 else if (e.Message.Contains("404")) 87 requestResult.Message = "連接伺服器404,請重試"; 88 else 89 requestResult.Message = e.Message; 90 callback?.Invoke(requestResult); 91 } 92 } 93 94 public static async void AsyncPut(string url, string body, Action<RequestResult> callback, int timeoutSeconds = 10) 95 { 96 var requestResult = new RequestResult(); 97 if (!IsConnectedInternet()) 98 { 99 requestResult.Message = "網路連接錯誤,請稍後再試。"; 100 callback?.Invoke(requestResult); 101 return; 102 } 103 104 try 105 { 106 using (var client = new HttpClient()) 107 { 108 client.DefaultRequestHeaders.Add("authorization", "LJQfL1A2oeP2fuEiOHo6"); 109 client.Timeout = new TimeSpan(0, 0, timeoutSeconds); 110 HttpContent content = new StringContent(body); 111 content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); 112 var response = await client.PutAsync(url, content); 113 //確保HTTP成功狀態值 114 response.EnsureSuccessStatusCode(); 115 //await非同步讀取最後的JSON 116 await response.Content.ReadAsStringAsync().ContinueWith(t => 117 { 118 if (t.IsCompleted) 119 { 120 requestResult.IsSuccess = true; 121 requestResult.Result = t.Result; 122 callback?.Invoke(requestResult); 123 } 124 }); 125 } 126 } 127 catch (Exception e) 128 { 129 if (e.InnerException != null) 130 requestResult.Message = e.InnerException.Message; 131 else if (e.Message.Contains("已取消一個任務")) 132 requestResult.Message = "連接伺服器超時,請重試"; 133 else if (e.Message.Contains("404")) 134 requestResult.Message = "連接伺服器404,請重試"; 135 else 136 requestResult.Message = e.Message; 137 callback?.Invoke(requestResult); 138 } 139 } 140 141 public static async void AsyncDelete(string url, Action<RequestResult> callback, int timeoutSeconds = 10) 142 { 143 var requestResult = new RequestResult(); 144 if (!IsConnectedInternet()) 145 { 146 requestResult.Message = "網路連接錯誤,請稍後再試。"; 147 callback?.Invoke(requestResult); 148 return; 149 } 150 151 try 152 { 153 using (var client = new HttpClient()) 154 { 155 client.DefaultRequestHeaders.Add("authorization", "LJQfL1A2oeP2fuEiOHo6"); 156 client.Timeout = new TimeSpan(0, 0, timeoutSeconds); 157 var response = await client.DeleteAsync(url); 158 //確保HTTP成功狀態值 159 response.EnsureSuccessStatusCode(); 160 //await非同步讀取最後的JSON 161 await response.Content.ReadAsStringAsync().ContinueWith(t => 162 { 163 if (t.IsCompleted) 164 { 165 requestResult.IsSuccess = true; 166 requestResult.Result = t.Result; 167 callback?.Invoke(requestResult); 168 } 169 }); 170 } 171 } 172 catch (Exception e) 173 { 174 if (e.InnerException != null) 175 requestResult.Message = e.InnerException.Message; 176 else if (e.Message.Contains("已取消一個任務")) 177 requestResult.Message = "連接伺服器超時,請重試"; 178 else if (e.Message.Contains("404")) 179 requestResult.Message = "連接伺服器404,請重試"; 180 else 181 requestResult.Message = e.Message; 182 callback?.Invoke(requestResult); 183 } 184 }
上面使用到的RequestResult類:
1 public class RequestResult : IDisposable 2 { 3 public bool IsSuccess { get; set; } 4 5 public string Result { get; set; } 6 7 public string Message { get; set; } 8 9 public RequestResult(bool isSuccess = false, string result = "", string message = "") 10 { 11 IsSuccess = isSuccess; 12 Result = result; 13 Message = message; 14 } 15 16 ~RequestResult() 17 { 18 Dispose(); 19 } 20 21 public void Dispose() 22 { 23 Dispose(true); 24 GC.SuppressFinalize(this);//不需要再調用本對象的Finalize方法 25 } 26 27 protected virtual void Dispose(Boolean disposing) 28 { 29 if (disposing) 30 { 31 //--- 清理托管資源 ---// 32 } 33 34 //--- 清理非托管資源 ---// 35 } 36 }
還有一個判斷Windows系統網路連接狀態的方法:
1 #region 網路狀態測試 2 3 [DllImport("winInet.dll")] 4 private static extern bool InternetGetConnectedState(ref int dwFlag, int dwReserved); 5 6 /// <summary> 7 /// 用於檢查網路是否可以連接互聯網,true表示連接成功,false表示連接失敗 8 /// </summary> 9 /// <returns></returns> 10 private static bool IsConnectedInternet() 11 { 12 int description = 0; 13 return InternetGetConnectedState(ref description, 0); 14 } 15 16 #endregion
--------------------------------------------------------------------------------------我是分割線-----------------------------------------------------------------------------------------------------------
最後是使用WebClient進行文件下載,這裡使用了Task非同步方式,也可以改為普通方法或靜態方法:
1 /// <summary> 2 /// 下載文件 3 /// </summary> 4 /// <param name="fileUrl">文件地址</param> 5 /// <param name="filePath">文件的本地路徑</param> 6 /// <returns>文件在本地的存儲路徑</returns> 7 private Task GetFileLocalPath(