PostObjectRequest.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using COSXML.Common;
  5. using System.IO;
  6. using COSXML.Utils;
  7. using COSXML.CosException;
  8. using COSXML.Auth;
  9. using COSXML.Log;
  10. using COSXML.Network;
  11. namespace COSXML.Model.Object
  12. {
  13. /// <summary>
  14. /// 使用者用表单的形式将文件(Object)上传至指定 Bucket 中.
  15. /// <see href="https://cloud.tencent.com/document/product/436/14690"/>
  16. /// </summary>
  17. public sealed class PostObjectRequest : ObjectRequest
  18. {
  19. /// <summary>
  20. /// 表单字段
  21. /// <see href="FormStruct"/>
  22. /// </summary>
  23. private FormStruct formStruct;
  24. private PostObjectRequest(string bucket, string key)
  25. : base(bucket, "/")
  26. {
  27. this.method = CosRequestMethod.POST;
  28. formStruct = new FormStruct();
  29. formStruct.key = key;
  30. this.SetHeader(CosRequestHeaderKey.CONTENT_TYPE, "multipart/form-data; boundary=" + MultipartRequestBody.BOUNDARY);
  31. this.needMD5 = false;
  32. }
  33. /// <summary>
  34. /// 上传文件
  35. /// </summary>
  36. /// <param name="bucket"></param>
  37. /// <param name="key"></param>
  38. /// <param name="srcPath"></param>
  39. public PostObjectRequest(string bucket, string key, string srcPath)
  40. : this(bucket, key, srcPath, -1L, -1L)
  41. {
  42. }
  43. /// <summary>
  44. /// 上传文件的指定部分
  45. /// </summary>
  46. /// <param name="bucket"></param>
  47. /// <param name="key"></param>
  48. /// <param name="srcPath"></param>
  49. /// <param name="fileOffset">指定文件内容的起始位置</param>
  50. /// <param name="sendContentLength">指定文件内容的大小</param>
  51. public PostObjectRequest(string bucket, string key, string srcPath, long fileOffset, long sendContentLength)
  52. : this(bucket, key)
  53. {
  54. formStruct.srcPath = srcPath;
  55. formStruct.fileOffset = fileOffset < 0 ? 0 : fileOffset;
  56. formStruct.contentLength = sendContentLength < 0L ? -1L : sendContentLength;
  57. }
  58. /// <summary>
  59. /// 上传data数据
  60. /// </summary>
  61. /// <param name="bucket"></param>
  62. /// <param name="key"></param>
  63. /// <param name="data"></param>
  64. public PostObjectRequest(string bucket, string key, byte[] data)
  65. : this(bucket, key)
  66. {
  67. formStruct.data = data;
  68. }
  69. /// <summary>
  70. /// 设置进度回调
  71. /// </summary>
  72. /// <param name="progressCallback"></param>
  73. public void SetCosProgressCallback(COSXML.Callback.OnProgressCallback progressCallback)
  74. {
  75. formStruct.progressCallback = progressCallback;
  76. }
  77. /// <summary>
  78. /// 定义 Object 的 acl 属性。有效值:private,public-read-write,public-read;默认值:private
  79. /// <see href="Common.CosACL"/>
  80. /// </summary>
  81. /// <param name="cosACL"></param>
  82. public void SetCosACL(CosACL cosACL)
  83. {
  84. SetCosACL(EnumUtils.GetValue(cosACL));
  85. }
  86. /// <summary>
  87. /// 定义 Object 的 acl 属性。有效值:private,public-read-write,public-read;默认值:private
  88. /// <see href="Common.CosACL"/>
  89. /// </summary>
  90. /// <param name="cosACL"></param>
  91. public void SetCosACL(string cosACL)
  92. {
  93. if (cosACL != null)
  94. {
  95. formStruct.acl = cosACL;
  96. }
  97. }
  98. /// <summary>
  99. /// 设置对象的 cacheControl
  100. /// </summary>
  101. /// <param name="cacheControl"></param>
  102. public void SetCacheControl(string cacheControl)
  103. {
  104. SetHeader("Cache-Control", cacheControl);
  105. }
  106. /// <summary>
  107. /// 设置对象的contentType
  108. /// </summary>
  109. /// <param name="contentType"></param>
  110. public void SetContentType(string contentType)
  111. {
  112. SetHeader("Content-Type", contentType);
  113. }
  114. /// <summary>
  115. /// 设置对象的contentDisposition
  116. /// </summary>
  117. /// <param name="contentDisposition"></param>
  118. public void SetContentDisposition(string contentDisposition)
  119. {
  120. SetHeader("Content-Disposition", contentDisposition);
  121. }
  122. /// <summary>
  123. /// 设置对象的contentEncoding
  124. /// </summary>
  125. /// <param name="contentEncoding"></param>
  126. public void SetContentEncoding(string contentEncoding)
  127. {
  128. SetHeader("Content-Encoding", contentEncoding);
  129. }
  130. /// <summary>
  131. /// 设置对象 Expire
  132. /// </summary>
  133. /// <param name="expires"></param>
  134. public void SetExpires(string expires)
  135. {
  136. SetHeader("Expires", expires);
  137. }
  138. /// <summary>
  139. /// 设置对象header属性
  140. /// </summary>
  141. /// <param name="key"></param>
  142. /// <param name="value"></param>
  143. public void SetHeader(string key, string value)
  144. {
  145. try
  146. {
  147. formStruct.headers.Add(key, value);
  148. }
  149. catch (ArgumentException)
  150. {
  151. formStruct.headers[key] = value;
  152. }
  153. }
  154. /// <summary>
  155. /// 设置对象自定义的header属性
  156. /// </summary>
  157. /// <param name="key"></param>
  158. /// <param name="value"></param>
  159. public void SetCustomerHeader(string key, string value)
  160. {
  161. try
  162. {
  163. formStruct.customHeaders.Add(key, value);
  164. }
  165. catch (ArgumentException)
  166. {
  167. formStruct.customHeaders[key] = value;
  168. }
  169. }
  170. /// <summary>
  171. /// 设置对象的存储类型
  172. /// <see href="Common.CosStorageClass"/>
  173. /// </summary>
  174. /// <param name="cosStorageClass"></param>
  175. public void SetCosStorageClass(string cosStorageClass)
  176. {
  177. formStruct.xCosStorageClass = cosStorageClass;
  178. }
  179. /// <summary>
  180. /// 最大上传速度,单位是 bit/s
  181. /// </summary>
  182. /// <param name="rate"></param>
  183. public void LimitTraffic(long rate)
  184. {
  185. formStruct.xCOSTrafficLimit = rate.ToString();
  186. }
  187. /// <summary>
  188. /// 若设置优先生效,返回 303 并提供 Location 头部,
  189. /// 会在 URL 尾部加上 bucket={bucket}&lt;key={key}&gt;etag={%22etag%22} 参数。
  190. /// </summary>
  191. /// <param name="redirectHost"></param>
  192. public void SetSuccessActionRedirect(string redirectHost)
  193. {
  194. formStruct.successActionRedirect = redirectHost;
  195. }
  196. /// <summary>
  197. /// successHttpCode can be 200, 201, 204, default value 204
  198. /// 若填写 success_action_redirect 则会略此设置。
  199. /// </summary>
  200. /// <param name="successHttpCode"></param>
  201. public void SetSuccessActionStatus(int successHttpCode)
  202. {
  203. formStruct.successActionStatus = successHttpCode.ToString();
  204. }
  205. /// <summary>
  206. /// 用于做请求检查,如果请求的内容和 Policy 指定的条件不符,返回 403 AccessDenied。
  207. /// <see href="Policy"/>
  208. /// </summary>
  209. /// <param name="policy"></param>
  210. public void SetPolicy(Policy policy)
  211. {
  212. formStruct.policy = policy;
  213. }
  214. public override void CheckParameters()
  215. {
  216. formStruct.CheckParameter();
  217. base.CheckParameters();
  218. }
  219. public override CosXmlSignSourceProvider GetSignSourceProvider()
  220. {
  221. var signSourceProvider = base.GetSignSourceProvider();
  222. if (signSourceProvider != null)
  223. {
  224. signSourceProvider.RemoveHeaderKey("content-type");
  225. signSourceProvider.RemoveHeaderKey("content-length");
  226. signSourceProvider.RemoveHeaderKey("content-md5");
  227. signSourceProvider.onGetSign = delegate (Request request, string sign)
  228. {
  229. //添加参数 sign
  230. ((MultipartRequestBody)request.Body).AddParameter("Signature", sign);
  231. };
  232. }
  233. return signSourceProvider;
  234. }
  235. public override RequestBody GetRequestBody()
  236. {
  237. MultipartRequestBody requestBody = new MultipartRequestBody();
  238. requestBody.AddParamters(formStruct.GetFormParameters());
  239. if (formStruct.data != null)
  240. {
  241. requestBody.AddData(formStruct.data, "file", "tmp");
  242. }
  243. else if (formStruct.srcPath != null)
  244. {
  245. FileInfo fileInfo = new FileInfo(this.formStruct.srcPath);
  246. string fileName = fileInfo.Name;
  247. if (formStruct.contentLength == -1L || formStruct.contentLength + formStruct.fileOffset > fileInfo.Length)
  248. {
  249. formStruct.contentLength = fileInfo.Length - formStruct.fileOffset;
  250. }
  251. requestBody.AddData(formStruct.srcPath, formStruct.fileOffset, formStruct.contentLength, "file", fileName);
  252. }
  253. requestBody.ProgressCallback = formStruct.progressCallback;
  254. return requestBody;
  255. }
  256. private class FormStruct
  257. {
  258. /// <summary>
  259. /// 对象的ACL
  260. /// </summary>
  261. public string acl;
  262. /// <summary>
  263. /// 对象的header元数据
  264. /// </summary>
  265. public Dictionary<string, string> headers;
  266. /// <summary>
  267. /// 上传后的文件名,使用 ${filename} 则会进行替换。
  268. /// 例如a/b/${filename},上传文件 a1.txt,那么最终的上传路径就是 a/b/a1.txt
  269. /// </summary>
  270. public string key;
  271. /// <summary>
  272. /// 若设置优先生效,返回 303 并提供 Location 头部
  273. /// </summary>
  274. public string successActionRedirect;
  275. /// <summary>
  276. /// 可选 200,201,204 默认返回 204。若填写 success_action_redirect 则会略此设置。
  277. /// </summary>
  278. public string successActionStatus;
  279. /// <summary>
  280. /// 对象的自定义元数据
  281. /// </summary>
  282. public Dictionary<string, string> customHeaders;
  283. /// <summary>
  284. /// 对象存储类型
  285. /// </summary>
  286. public string xCosStorageClass;
  287. /// <summary>
  288. /// 速度限制
  289. /// </summary>
  290. public string xCOSTrafficLimit;
  291. /// <summary>
  292. /// 请求检查策略
  293. /// <see href="Policy"/>
  294. /// </summary>
  295. public Policy policy;
  296. /// <summary>
  297. /// 上传文件的本地路径
  298. /// </summary>
  299. public string srcPath;
  300. /// <summary>
  301. /// 上传文件指定起始位置
  302. /// </summary>
  303. public long fileOffset = 0L;
  304. /// <summary>
  305. /// 上传文件指定内容大小
  306. /// </summary>
  307. public long contentLength = -1L;
  308. /// <summary>
  309. /// 上传data数据
  310. /// </summary>
  311. public byte[] data;
  312. /// <summary>
  313. /// 上传回调
  314. /// </summary>
  315. public COSXML.Callback.OnProgressCallback progressCallback;
  316. public FormStruct()
  317. {
  318. headers = new Dictionary<string, string>();
  319. customHeaders = new Dictionary<string, string>();
  320. }
  321. public Dictionary<string, string> GetFormParameters()
  322. {
  323. Dictionary<string, string> formParameters = new Dictionary<string, string>();
  324. if (acl != null)
  325. {
  326. formParameters.Add("Acl", acl);
  327. }
  328. foreach (KeyValuePair<string, string> pair in headers)
  329. {
  330. formParameters.Add(pair.Key, pair.Value);
  331. }
  332. formParameters.Add("key", key);
  333. if (successActionRedirect != null)
  334. {
  335. formParameters.Add("success_action_redirect", successActionRedirect);
  336. }
  337. if (successActionStatus != null)
  338. {
  339. formParameters.Add("success_action_status", successActionStatus);
  340. }
  341. foreach (KeyValuePair<string, string> pair in customHeaders)
  342. {
  343. formParameters.Add(pair.Key, pair.Value);
  344. }
  345. if (xCosStorageClass != null)
  346. {
  347. formParameters.Add("x-cos-storage-class", xCosStorageClass);
  348. }
  349. if (xCOSTrafficLimit != null)
  350. {
  351. formParameters.Add(CosRequestHeaderKey.X_COS_TRAFFIC_LIMIT, xCOSTrafficLimit);
  352. }
  353. if (policy != null)
  354. {
  355. formParameters.Add("policy", DigestUtils.GetBase64(policy.Content(), Encoding.UTF8));
  356. }
  357. return formParameters;
  358. }
  359. public void CheckParameter()
  360. {
  361. if (String.IsNullOrEmpty(key))
  362. {
  363. throw new CosClientException((int)CosClientError.InvalidArgument, "FormStruct.key(null or empty) is invalid");
  364. }
  365. if (srcPath == null && data == null)
  366. {
  367. throw new CosClientException((int)CosClientError.InvalidArgument, "data source = null");
  368. }
  369. if (srcPath != null)
  370. {
  371. if (!File.Exists(srcPath))
  372. {
  373. throw new CosClientException((int)CosClientError.InvalidArgument, "srcPath not exist");
  374. }
  375. }
  376. }
  377. }
  378. public class Policy
  379. {
  380. /// <summary>
  381. /// 过期时间
  382. /// </summary>
  383. private string expiration;
  384. /// <summary>
  385. /// 检查条件
  386. /// </summary>
  387. private StringBuilder conditions = new StringBuilder();
  388. public void SetExpiration(long endTimeMills)
  389. {
  390. this.expiration = TimeUtils_QCloud.GetFormatTime("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", endTimeMills, TimeUnit.Milliseconds);
  391. }
  392. public void AddConditions(string key, string value, bool isPrefixMatch)
  393. {
  394. if (conditions.Length > 0)
  395. {
  396. conditions.Append(',');
  397. }
  398. if (isPrefixMatch)
  399. {
  400. conditions.Append('[')
  401. .Append("\"starts-with\"")
  402. .Append(",\"")
  403. .Append(key)
  404. .Append("\",\"")
  405. .Append(value)
  406. .Append("\"]");
  407. }
  408. else
  409. {
  410. conditions.Append("{\"")
  411. .Append(key)
  412. .Append("\":\"")
  413. .Append(value)
  414. .Append("\"}");
  415. }
  416. }
  417. public void AddContentConditions(int start, int end)
  418. {
  419. if (conditions.Length > 0)
  420. {
  421. conditions.Append(',');
  422. }
  423. conditions.Append('[')
  424. .Append("\"content-length-range\"")
  425. .Append(',')
  426. .Append(start)
  427. .Append(',')
  428. .Append(end)
  429. .Append(']');
  430. }
  431. public string Content()
  432. {
  433. StringBuilder content = new StringBuilder();
  434. content.Append('{');
  435. if (expiration != null)
  436. {
  437. content.Append(String.Format("\"expiration\":\"{0}\"", expiration));
  438. }
  439. if (conditions.Length > 0)
  440. {
  441. if (expiration != null)
  442. {
  443. content.Append(",");
  444. }
  445. content.Append(String.Format("\"conditions\":[{0}]", conditions));
  446. }
  447. content.Append('}');
  448. return content.ToString();
  449. }
  450. }
  451. }
  452. }