最近,因公司項目需要對接天翼雲OOS,在百度多次折騰后,大部分的都是基於java、php 等其他語言,很少基於C#語言的相關資料,即使有也是基於.NET Framwork開發的SDK,內容幾乎是千篇一律,很少基於.NET CORE的開發。在官網上也很少發現基於C#語言的身影,最終在網上找尋到OOS相關的SDK集合中找到基於.NET(C#) SDK開發包 。
根據SDK開發包指引迫不及待的在.NET CORE 項目中嘗試,但最終還是以失敗告終。然后再.NET Framework 環境中居然能成功,百思不得其解,於是聯系聯系電信售后那邊,最終從電信技術人員口中得知,不支持.NET CORE。
最終,參考OOS開發者文檔,采用調用http接口的方式實現,惱火的部分,不是接口的調用,而是簽名算法,這個折騰了好幾天,最終采用將文檔中的java代碼翻譯成C#代碼搞定。第一次寫博客,那直接就上代碼,通過下面的封裝既可以在.NET Framework中使用,也可以在.NET core中使用,
第一步:實現簽名算法:
/// <summary> /// 天翼雲OOS簽名V2版本 /// </summary> public class CtyunOOSSignatureV2 { /// <summary> /// 賬號 /// </summary> private String secretkey; /// <summary> /// 秘鑰 /// </summary> private string accessKey; /// <summary> /// /// </summary> /// <param name="secretkey">秘鑰</param> /// <param name="accessKey">賬號</param> /// <param name="region"></param> public CtyunOOSSignatureV2(string accessKey,String secretkey) { this.secretkey = secretkey; this.accessKey = accessKey; } /// <summary> /// HMACSHA1算法加密並返回ToBase64String /// </summary> /// <param name="strText">簽名參數字符串</param> /// <param name="strKey">密鑰參數</param> /// <returns>返回一個簽名值(即哈希值)</returns> private string ToBase64hmac(string strKey, string strText) { HMACSHA1 myHMACSHA1 = new HMACSHA1(Encoding.UTF8.GetBytes(strKey)); byte[] byteText = myHMACSHA1.ComputeHash(Encoding.UTF8.GetBytes(strText)); return System.Convert.ToBase64String(byteText); } private string GetStringToSign(string httpVerb, String contentType, string date, string uri) { String stringToSign = httpVerb + "\n\n"+ contentType + "\n" + date + "\n" + uri; return stringToSign; } /// <summary> /// 獲取認證簽名信息 /// </summary> /// <param name="httpVerb">請求方法</param> /// <param name="date">請求時間</param> /// <param name="uri">存儲桶</param> /// <param name="objectname">存儲對象名稱</param> /// <returns></returns> public string AuthorizationSignature(string httpVerb,String contentType, string date, string uri) { var stringToSign = GetStringToSign(httpVerb, contentType,date, uri); var signature = ToBase64hmac(this.secretkey, stringToSign); return "AWS " + this.accessKey + ":" + signature; } }
第二步:實現接口調用:
public interface IOOSHelper { /// <summary> /// 設置配置信息 /// </summary> /// <param name="serviceURL">雲地址</param> /// <param name="accessKey">oos賬號</param> /// <param name="secretKey">oos密碼</param> /// <param name="bucketName">存儲桶</param> void InitOOSHelper(string serviceURL, string accessKey, string secretKey, string bucketName); /// <summary> /// 上傳文件 /// </summary> /// <param name="stream">文件流</param> /// <param name="objectname">商戶號+文件名稱,例如:99999/1.JPG</param> /// <param name="msg">如果成功返回文件存儲地址;失敗返回錯誤原因</param> /// <returns></returns> Boolean UploadFile(Stream inputStream, string objectname,ref string msg); }
public class CtyunOOSHttpHelper : IOOSHelper { private readonly String root_dir = "WeChatApp"; /// <summary> /// 天翼雲oos賬號 /// </summary> private string _accessKey; /// <summary> /// 天翼雲oos密碼 /// </summary> private string _secretKey; /// <summary> /// 存儲桶 /// </summary> private string _bucketName; /// <summary> /// 天翼雲地址 /// </summary> private string _serviceURL; private CtyunOOSSignatureV2 signature; public void InitOOSHelper(string serviceURL, string accessKey, string secretKey, string bucketName) { this._serviceURL = serviceURL; this._accessKey = accessKey; this._secretKey = secretKey; this._bucketName = bucketName; signature = new CtyunOOSSignatureV2(accessKey, secretKey); } /// <summary> /// 上傳文件 /// </summary> /// <param name="stream">文件流</param> /// <param name="objectname">商戶號+文件名稱,例如:99999/1.JPG</param> /// <param name="msg">如果成功返回文件存儲地址;失敗返回錯誤原因</param> /// <returns></returns> public bool UploadFile(Stream stream, string objectname,ref String msg) { Boolean success = true; int filelength = 0; filelength = (int)stream.Length; //獲得文件長度 byte[] data = new Byte[filelength]; //建立一個字節數組 stream.Read(data, 0, filelength); //按字節流讀取 HttpRequestHelper httpReqHelper = new HttpRequestHelper(this._serviceURL); string uri = this._bucketName + "/" + root_dir+ "/" + objectname; String datetime = DateTime.Now.ToUniversalTime().ToString("R"); var authorization = signature.AuthorizationSignature(HttpRequestHelper.HttpType.PUT.ToString(), "image/jpeg", datetime, "/" + uri); Dictionary<String, string> headers = new Dictionary<string, string>(); headers.Add("Date", datetime); headers.Add("Content-Type", "image/jpeg"); headers.Add("Authorization", authorization); try { httpReqHelper.AddRequestHeaders(headers); msg= httpReqHelper.HttpRequest(uri, HttpRequestHelper.HttpType.PUT, data); if (String.IsNullOrEmpty(msg)) msg = this._serviceURL +"/"+ uri; } catch (Exception ex) { msg= ex.Message; success = false; } return success; } }
第三步:編寫單元測試
[TestClass] public class UnitOOSTest { private static String accessKey = "b7f***********bb13c4f6"; private static String secretKey = "9e1739f4986ba***********a4f85b20bdcf"; // API 服務器 private static String OOS_SERVICE = "http://oos-***.ctyunapi.cn"; private static String bucketName = "dx-****-dev"; [TestMethod] public void TestOOSUploadFile() { IOOSHelper ctyunOOSHelper = new CtyunOOSHttpHelper(); ctyunOOSHelper.InitOOSHelper(OOS_SERVICE, accessKey, secretKey, bucketName); string msg=string.Empty; using (var fs = new FileStream(@"f:\181256.jpg", FileMode.Open)) { ctyunOOSHelper.UploadFile(fs, "1121212/181256.jpg", ref msg); } Console.ReadKey(); } }
以上即是調用OOS的全部代碼。
以上項目中用到httphelper幫助類:
public class HttpRequestHelper { private static string BaseUri; /// <summary> /// /// </summary> private Dictionary<String, string> headers; public HttpRequestHelper(string baseUri) { BaseUri = baseUri; } #region Delete方式 private string Delete(string uri, byte[] data) { string serviceUrl = ""; if (BaseUri == "" || BaseUri == null) { serviceUrl = uri; } else { serviceUrl = string.Format("{0}/{1}", BaseUri, uri); } return CommonHttpRequest(serviceUrl, "DELETE", data); } #endregion #region Put方式 private string Put(string uri, byte[] data) { string serviceUrl = ""; if (BaseUri == "" || BaseUri == null) { serviceUrl = uri; } else { serviceUrl = string.Format("{0}/{1}", BaseUri, uri); } return CommonHttpRequest(serviceUrl, "PUT", data); } #endregion #region POST方式實現 private string Post(string uri, byte[] data) { string serviceUrl = ""; if (BaseUri == "" || BaseUri == null) { serviceUrl = uri; } else { serviceUrl = string.Format("{0}/{1}", BaseUri, uri); } return CommonHttpRequest(serviceUrl, "Post", data); } #endregion #region GET方式實現 private string Get(string uri) { string serviceUrl = ""; if (BaseUri == "" || BaseUri == null) { serviceUrl = uri; } else { serviceUrl = string.Format("{0}/{1}", BaseUri, uri); } return CommonHttpRequest(serviceUrl, "GET", null); } #endregion #region 私有方法 private string CommonHttpRequest(string url, string reqType, byte[] data) { HttpWebRequest webRequest = null; Stream outstream = null; HttpWebResponse myResponse = null; StreamReader reader = null; try { //構造http請求的對象 webRequest = (HttpWebRequest)WebRequest.Create(url); //設置 webRequest.ProtocolVersion = HttpVersion.Version11; webRequest.Method = reqType; if(headers !=null && headers.Count>0) { foreach(var header in headers) webRequest.Headers.Add(header.Key,header.Value); } if (data !=null&&data.Length >0) { webRequest.ContentLength = data.Length; //轉成網絡流 byte[] buf = data; outstream = webRequest.GetRequestStream(); outstream.Flush(); outstream.Write(buf, 0, buf.Length); outstream.Flush(); outstream.Close(); } // 獲得接口返回值 myResponse = (HttpWebResponse)webRequest.GetResponse(); reader = new StreamReader(myResponse.GetResponseStream(), Encoding.UTF8); string ReturnXml = reader.ReadToEnd(); reader.Close(); myResponse.Close(); webRequest.Abort(); return ReturnXml; } catch (Exception ex) { if (outstream != null) outstream.Close(); if (reader != null) reader.Close(); if (myResponse != null) myResponse.Close(); if (webRequest != null) webRequest.Abort(); throw ex; } } #endregion #region 通用請求 /// <summary> /// Http通用請求 /// </summary> /// <param name="url"></param> /// <param name="type"></param> /// <param name="data"></param> /// <returns></returns> public string HttpRequest(string url, HttpType type, byte[] data) { switch (type) { case HttpType.PUT: return Put(url, data); case HttpType.GET: return Get(url); case HttpType.POST: return Post(url, data); case HttpType.DELETE: return Delete(url, data); default: break; } return ""; } /// <summary> /// 添加請求頭 /// </summary> /// <param name="headers"></param> public void AddRequestHeaders(Dictionary<String,string> headers) { this.headers = headers; } /// <summary> /// Http通用請求 /// </summary> /// <param name="ip"></param> /// <param name="port"></param> /// <param name="uri"></param> /// <param name="type"></param> /// <param name="data"></param> /// <returns></returns> public string HttpRequest(string ip, string port, string uri, HttpType type, byte[] data) { string url = "http://" + ip + ":" + port + uri; return HttpRequest(url, type, data); } #endregion public enum HttpType { PUT = 0, GET = 1, POST = 2, DELETE = 3 } }
注意事項:
1、根據本人與天翼雲技術溝通,目前他們注意是支持的oos開發者文檔中的v2簽名格式,v4測試過幾次,並沒有通過,如果有實現了的小伙伴可以分享一下,不足之處請多多指教!
2、注意系統調用的時間應該采用DateTime.Now.ToUniversalTime(),而不是DateTime.Now,如果時間不對也無法上傳;
3、由於是采用的異步上傳,其實文件上傳后的路徑,即為請求地址+文件路徑。