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