OssUtils.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /*
  2. * Copyright (C) Alibaba Cloud Computing
  3. * All rights reserved.
  4. *
  5. * 版权所有 (C)阿里云计算有限公司
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Diagnostics;
  10. using System.IO;
  11. using System.Text;
  12. using System.Security.Cryptography;
  13. using System.Text.RegularExpressions;
  14. using Aliyun.OSS.Common;
  15. using Aliyun.OSS.Common.Communication;
  16. using Aliyun.OSS.Commands;
  17. using Aliyun.OSS.Domain;
  18. using Aliyun.OSS.Properties;
  19. namespace Aliyun.OSS.Util
  20. {
  21. /// <summary>
  22. /// 一些SDK中常用的工具方法
  23. /// </summary>
  24. public static class OssUtils
  25. {
  26. private const string DefaultBaseChars = "0123456789ABCDEF";
  27. private const string CharsetName = "utf-8";
  28. /// <summary>
  29. /// 普通上传支持的最大文件大小:5G
  30. /// </summary>
  31. public const long MaxFileSize = 5 * 1024 * 1024 * 1024L;
  32. /// <summary>
  33. /// 前缀最大长度,1K
  34. /// </summary>
  35. public const int MaxPrefixStringSize = 1024;
  36. /// <summary>
  37. /// marker最大长度,1K
  38. /// </summary>
  39. public const int MaxMarkerStringSize = 1024;
  40. /// <summary>
  41. /// 分隔字符串最大长度,1K
  42. /// </summary>
  43. public const int MaxDelimiterStringSize = 1024;
  44. /// <summary>
  45. /// 最多返回的文件数,1000
  46. /// </summary>
  47. public const int MaxReturnedKeys = 1000;
  48. /// <summary>
  49. /// 一次最多删除的文件数,1000
  50. /// </summary>
  51. public const int DeleteObjectsUpperLimit = 1000;
  52. /// <summary>
  53. /// 存储空间的跨域资源共享规则数量限制,10
  54. /// </summary>
  55. public const int BucketCorsRuleLimit = 10;
  56. /// <summary>
  57. /// 生命周期规则数量限制,1000
  58. /// </summary>
  59. public const int LifecycleRuleLimit = 1000;
  60. /// <summary>
  61. /// 文件长度最长限制,1023
  62. /// </summary>
  63. public const int ObjectNameLengthLimit = 1023;
  64. /// <summary>
  65. /// 分片个数限制,10000
  66. /// </summary>
  67. public const int PartNumberUpperLimit = 10000;
  68. /// <summary>
  69. /// 默认分片大小,8M
  70. /// </summary>
  71. public const long DefaultPartSize = 8 * 1024 * 1024;
  72. /// <summary>
  73. /// 分片上传或者拷贝时,最小分片大小,100K
  74. /// </summary>
  75. public const long PartSizeLowerLimit = 100 * 1024;
  76. /// <summary>
  77. /// 判断存储空间(bucket)名字是否合法
  78. /// </summary>
  79. /// <param name="bucketName">存储空间的名称</param>
  80. /// <returns>是否合法</returns>
  81. public static bool IsBucketNameValid(string bucketName)
  82. {
  83. if (string.IsNullOrEmpty(bucketName))
  84. return false;
  85. const string pattern = "^[a-z0-9][a-z0-9\\-]{1,61}[a-z0-9]$";
  86. var regex = new Regex(pattern);
  87. var m = regex.Match(bucketName);
  88. return m.Success;
  89. }
  90. /// <summary>
  91. /// 判断文件名是否合法
  92. /// </summary>
  93. /// <param name="key">文件名称</param>
  94. /// <returns>是否合法</returns>
  95. public static bool IsObjectKeyValid(string key)
  96. {
  97. if (string.IsNullOrEmpty(key) || key.StartsWith("/") || key.StartsWith("\\"))
  98. return false;
  99. var byteCount = Encoding.GetEncoding(CharsetName).GetByteCount(key);
  100. return byteCount <= ObjectNameLengthLimit;
  101. }
  102. internal static String MakeResourcePath(Uri endpoint, string bucket, string key)
  103. {
  104. String resourcePath = (key == null) ? string.Empty : key;
  105. if (IsIp(endpoint))
  106. {
  107. resourcePath = bucket + "/" + resourcePath;
  108. }
  109. return UrlEncodeKey(resourcePath);
  110. }
  111. internal static Uri MakeBucketEndpoint(Uri endpoint, string bucket, ClientConfiguration conf)
  112. {
  113. var uri = new Uri(endpoint.Scheme + "://"
  114. + ((bucket != null && !conf.IsCname && !IsIp(endpoint))
  115. ? (bucket + "." + endpoint.Host) : endpoint.Host)
  116. + ((endpoint.Port != 80) ? (":" + endpoint.Port) : ""));
  117. return uri;
  118. }
  119. /// <summary>
  120. /// 判断endpoint是否是ip形式
  121. /// </summary>
  122. /// <param name="endpoint">endpoint的值</param>
  123. /// <returns>是否是ip</returns>
  124. private static bool IsIp(Uri endpoint)
  125. {
  126. return Regex.IsMatch(endpoint.Host, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$");
  127. }
  128. /// <summary>
  129. /// 对key做编码
  130. /// </summary>
  131. /// <param name="key">需要编码的字符</param>
  132. /// <returns>编码后的字符</returns>
  133. private static String UrlEncodeKey(String key)
  134. {
  135. const char separator = '/';
  136. var segments = key.Split(separator);
  137. var encodedKey = new StringBuilder();
  138. encodedKey.Append(HttpUtils.EncodeUri(segments[0], CharsetName));
  139. for (var i = 1; i < segments.Length; i++)
  140. encodedKey.Append(separator).Append(HttpUtils.EncodeUri(segments[i], CharsetName));
  141. if (key.EndsWith(separator.ToString()))
  142. {
  143. // String#split ignores trailing empty strings, e.g., "a/b/" will be split as a 2-entries array,
  144. // so we have to append all the trailing slash to the uri.
  145. foreach (var ch in key)
  146. {
  147. if (ch == separator)
  148. encodedKey.Append(separator);
  149. else
  150. break;
  151. }
  152. }
  153. return encodedKey.ToString();
  154. }
  155. /// <summary>
  156. /// 删掉eTag起始和结束处的引号
  157. /// </summary>
  158. /// <param name="eTag">需要删除引号的eTag值</param>
  159. /// <returns>删除起始和结束处的引号后的值</returns>
  160. public static string TrimQuotes(string eTag)
  161. {
  162. return eTag != null ? eTag.Trim('\"') : null;
  163. }
  164. /// <summary>
  165. /// 对输入流计算md5值
  166. /// </summary>
  167. /// <param name="input">待计算的输入流</param>
  168. /// <param name="partSize">计算多长的输入流</param>
  169. /// <returns>流的md5值</returns>
  170. public static string ComputeContentMd5(Stream input, long partSize)
  171. {
  172. using (var md5 = MD5.Create())
  173. {
  174. int readSize = (int)partSize;
  175. long pos = input.Position;
  176. byte[] buffer = new byte[readSize];
  177. readSize = input.Read(buffer, 0, readSize);
  178. var data = md5.ComputeHash(buffer, 0, readSize);
  179. var charset = DefaultBaseChars.ToCharArray();
  180. var sBuilder = new StringBuilder();
  181. foreach (var b in data)
  182. {
  183. sBuilder.Append(charset[b >> 4]);
  184. sBuilder.Append(charset[b & 0x0F]);
  185. }
  186. input.Seek(pos, SeekOrigin.Begin);
  187. return Convert.ToBase64String(data);
  188. }
  189. }
  190. /// <summary>
  191. /// 判断webpage是否合法
  192. /// </summary>
  193. /// <param name="webpage">需要验证的webpage值</param>
  194. /// <returns>是否合法</returns>
  195. public static bool IsWebpageValid(string webpage)
  196. {
  197. const string pageSuffix = ".html";
  198. return !string.IsNullOrEmpty(webpage) && webpage.EndsWith(pageSuffix)
  199. && webpage.Length > pageSuffix.Length;
  200. }
  201. /// <summary>
  202. /// 判断日志前缀是否合法
  203. /// </summary>
  204. /// <param name="loggingPrefix">需要判断的日志前缀</param>
  205. /// <returns>是否合法</returns>
  206. public static bool IsLoggingPrefixValid(string loggingPrefix)
  207. {
  208. if (string.IsNullOrEmpty(loggingPrefix))
  209. return true;
  210. const string pattern = "^[a-zA-Z][a-zA-Z0-9\\-]{0,31}$";
  211. var regex = new Regex(pattern);
  212. var m = regex.Match(loggingPrefix);
  213. return m.Success;
  214. }
  215. internal static string BuildPartCopySource(string bucketName, string objectKey)
  216. {
  217. return "/" + bucketName + "/" + UrlEncodeKey(objectKey);
  218. }
  219. internal static string BuildCopyObjectSource(string bucketName, string objectKey)
  220. {
  221. return "/" + bucketName + "/" + UrlEncodeKey(objectKey);
  222. }
  223. internal static bool IsPartNumberInRange(int? partNumber)
  224. {
  225. return (partNumber.HasValue && partNumber > 0
  226. && partNumber <= OssUtils.PartNumberUpperLimit);
  227. }
  228. internal static void CheckBucketName(string bucketName)
  229. {
  230. if (string.IsNullOrEmpty(bucketName))
  231. throw new ArgumentException(Resources.ExceptionIfArgumentStringIsNullOrEmpty, "bucketName");
  232. if (!IsBucketNameValid(bucketName))
  233. throw new ArgumentException(OssResources.BucketNameInvalid, "bucketName");
  234. }
  235. internal static void CheckObjectKey(string key)
  236. {
  237. if (string.IsNullOrEmpty(key))
  238. throw new ArgumentException(Resources.ExceptionIfArgumentStringIsNullOrEmpty, "key");
  239. if (!IsObjectKeyValid(key))
  240. throw new ArgumentException(OssResources.ObjectKeyInvalid, "key");
  241. }
  242. internal static string DetermineOsVersion()
  243. {
  244. try
  245. {
  246. var os = Environment.OSVersion;
  247. return "windows " + os.Version.Major + "." + os.Version.Minor;
  248. }
  249. catch (InvalidOperationException /* ex */)
  250. {
  251. return "Unknown OSVersion";
  252. }
  253. }
  254. internal static string DetermineSystemArchitecture()
  255. {
  256. return (IntPtr.Size == 8) ? "x86_64" : "x86";
  257. }
  258. internal static string JoinETag(IEnumerable<string> etags)
  259. {
  260. StringBuilder result = new StringBuilder();
  261. var first = true;
  262. foreach (var etag in etags)
  263. {
  264. if (!first)
  265. result.Append(", ");
  266. result.Append(etag);
  267. first = false;
  268. }
  269. return result.ToString();
  270. }
  271. internal static ClientConfiguration GetClientConfiguration(IServiceClient serviceClient)
  272. {
  273. var outerClient = (RetryableServiceClient) serviceClient;
  274. var innerClient = (ServiceClient)outerClient.InnerServiceClient();
  275. return innerClient.Configuration;
  276. }
  277. internal static IAsyncResult BeginOperationHelper<TCommand>(TCommand cmd, AsyncCallback callback, Object state)
  278. where TCommand : OssCommand
  279. {
  280. var retryableAsyncResult = cmd.AsyncExecute(callback, state) as RetryableAsyncResult;
  281. if (retryableAsyncResult == null)
  282. {
  283. throw new ArgumentException("retryableAsyncResult should not be null");
  284. }
  285. return retryableAsyncResult.InnerAsyncResult;
  286. }
  287. internal static TResult EndOperationHelper<TResult>(IServiceClient serviceClient, IAsyncResult asyncResult)
  288. {
  289. var response = EndOperationHelper(serviceClient, asyncResult);
  290. var retryableAsyncResult = asyncResult as RetryableAsyncResult;
  291. Debug.Assert(retryableAsyncResult != null);
  292. OssCommand<TResult> cmd = (OssCommand<TResult>)retryableAsyncResult.Context.Command;
  293. return cmd.DeserializeResponse(response);
  294. }
  295. private static ServiceResponse EndOperationHelper(IServiceClient serviceClient, IAsyncResult asyncResult)
  296. {
  297. if (asyncResult == null)
  298. throw new ArgumentNullException("asyncResult");
  299. var retryableAsyncResult = asyncResult as RetryableAsyncResult;
  300. if (retryableAsyncResult == null)
  301. throw new ArgumentException("retryableAsyncResult should not be null");
  302. ServiceClientImpl.HttpAsyncResult httpAsyncResult =
  303. retryableAsyncResult.InnerAsyncResult as ServiceClientImpl.HttpAsyncResult;
  304. return serviceClient.EndSend(httpAsyncResult);
  305. }
  306. internal static void CheckCredentials(string accessKeyId, string accessKeySecret)
  307. {
  308. if (string.IsNullOrEmpty(accessKeyId))
  309. throw new ArgumentException(Resources.ExceptionIfArgumentStringIsNullOrEmpty, "accessKeyId");
  310. if (string.IsNullOrEmpty(accessKeySecret))
  311. throw new ArgumentException(Resources.ExceptionIfArgumentStringIsNullOrEmpty, "accessKeySecret");
  312. }
  313. }
  314. }