這幾天一直在寫Unity當中的C#的網絡傳輸,可是發現因爲Unity採用的mono框架,支持的僅僅是.Net Framework 3.5的API,並不支持更高級別的HttpClient,則不少功能須要手動開發。最先簡單測試過GET數據,而且JSON文件反序列化爲對象成功。以後實現了POST的JSON文件,獲取返回的JSON,因此麻痹大意,致使在uploadfile這個環節花費了大量的時間。apache
筆者認爲apache commons的庫設計很是好,因此仿照了httpclient進行了設計。json
└─Utils ├─GlobalConfig │ UnityTemplate.cs - 用來存儲Unity項目規範結構 │ ├─HttpHelper │ │ HttpHelper.cs - 針對訪問http程序的主要方法 │ │ │ ├─http │ │ HttpEntity.cs - HTTP Body中的內容 │ │ HttpHeader.cs - HTTP Headers │ │ │ └─methods │ HttpGet.cs - Get方法 │ HttpPost.cs - Post方法 │ HttpRequestBase.cs - 公共繼承方法 │ └─JsonHelper - 另外的庫,用來處理Json序列化與反序列化
根據HttpClient方法,因爲已經可以確認.net core開源版本已經集成HttpClient,若是將來Unity何時集成core,則不須要考慮如今的方法,則把如今的方法命名爲HttpHelper
HttpHelper主要方法爲execute,模仿httpclient,參數是存入的get或者post內容。因爲返回內容不肯定是鍵值對仍是json,則直接返回HttpWebResponse瀏覽器
public static HttpWebResponse execute(HttpRequestBase methods)
羅列了簡單的http header信息,並設置默認值,方便使用。網絡
public class HttpHeader { // 默認值通過http編碼 public string ContentType = "application/x- www-form-urlencoded"; public string UserAgent = "Unity/HttpHelper"; public long ContentLength; public System.Net.IWebProxy Proxy = null; public bool KeepAlive = true; }
因爲C#只可以經過ArrayList或者詞典,對不定類型的對象進行存儲,因此這裏使用了ArrayList存儲有可能發生的多種類型。
因爲即使是多種類型,依舊是採用鍵值對的方式進行訪問(json不經過鍵值對,不過會以鍵值對的方式聲明。)app
using System.Collections; using System.Collections.Generic; using System.IO; public class HttpEntity { private ArrayList entitys; public HttpEntity() { this.entitys = new ArrayList(); } public void setKeyValuePair(KeyValuePair<string, long> keyValuePair) { this.entitys.Add(keyValuePair); } public void setKeyValuePair(KeyValuePair<string,int> keyValuePair) { this.entitys.Add(keyValuePair); } public void setKeyValuePair(KeyValuePair<string,string> keyValuePair) { this.entitys.Add(keyValuePair); } public void setKeyValuePair(KeyValuePair<string,FileInfo> keyValuePair) { this.entitys.Add(keyValuePair); } public ArrayList getHttpElements() { return this.entitys; } public long getContentLength() { return -1; } public bool hasFile() { foreach(object item in this.entitys) { if (item is KeyValuePair<string, FileInfo>) { return true; } } return false; } }
根據http的需求,須要生成分隔符,可是根據瀏覽器的建議,不會真正生成一個和文件內容毫無關係的字符串做爲分隔符。這樣作的緣由是要花費大量的時間去解決分隔符衝突的問題。因此通用作法是隨機找一個儘量複雜的分隔符下降重複的機率。
做者以guid的方式生成分隔符,使分隔符更復雜。框架
// 生成分隔符 string boundary = Guid.NewGuid().ToString("N");
KeyValuePair<string, FileInfo> kv = (KeyValuePair<string, FileInfo>)item; // HTTP規定,起始的分割線 string fileboundary = "--" + boundary + "\r\n"; httpbody_length += getLength(fileboundary); // HTTP規定,Content-Disposition string ContentDisposition = "Content-Disposition: form-data; name=\"" + kv.Key + "\"; filename=\"" + kv.Value.Name + "\"\r\n"; httpbody_length += getLength(ContentDisposition); // HTTP規定,ContentType string ContentType = "Content-Type: application/octet-stream" + "\r\n"; httpbody_length += getLength(ContentType); // HTTP規定,ContentTransferEncoding string ContentTransferEncoding = "ContentTransferEncoding:binary" + "\r\n"; httpbody_length += getLength(ContentTransferEncoding); // 區分文件頭與文件內容 string separator = "\r\n"; httpbody_length += getLength(separator); // 獲取文件長度 FileStream fileStream = new FileStream(kv.Value.FullName, FileMode.Open, FileAccess.Read); httpbody_length += fileStream.Length; fileStream.Close(); // 再次添加separator httpbody_length += getLength(separator);
HttpWebRequest必須先設置ContentLength這個參數,而後纔可以經過GetRequestStream獲取requestStream。
閱讀API,描述爲必須這樣,猜想實現是根據ContentLength對Stream進行初始化致使的。這種方式直接致使,沒法經過FileStream的大小來判斷最終的ContentLength大小。
解決方法:post
// 先計算body的長度,在寫入stream中 request.ContentLength = httpbody_length; // get http body bytes Stream requestStream = request.GetRequestStream();
在Unity端報400 request bad,因爲客戶端的限制,實質是看不到Unity的實際報錯內容。則須要到服務端去查看錯誤內容。測試
因爲須要手動編寫http的body規則,手誤致使編寫錯誤,而且沒有看到,致使調試好久。ui