最近,因公司项目需要对接天翼云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 + " "+ contentType + " " + date + " " + 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、由于是采用的异步上传,其实文件上传后的路径,即为请求地址+文件路径。