UploadFile.cs 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Net;
  4. using System.Net.Http.Headers;
  5. using System.Text;
  6. using WingServerCommon.Config;
  7. using WingServerCommon.Config.Parameters;
  8. using WingServerCommon.Log;
  9. namespace WingServerCommon.Interfaces.FileTransfer
  10. {
  11. /// <summary>
  12. /// 封装上传文件接口
  13. /// </summary>
  14. public class UploadFile
  15. {
  16. private readonly HttpClient _httpClient;
  17. //其他存储节点
  18. private List<string> _otherCloudStorageNodes;
  19. //自动分块大小5M
  20. private int _autoSliceSizeForUpload = 0;
  21. /// <summary>
  22. /// 默认构造
  23. /// </summary>
  24. public UploadFile(HttpClient client)
  25. {
  26. _httpClient = client;
  27. _otherCloudStorageNodes = new List<string>();
  28. }
  29. /// <summary>
  30. /// 上传文件
  31. /// </summary>
  32. /// <param name="fileName"></param>
  33. /// <param name="fileData"></param>
  34. /// <returns></returns>
  35. public async Task<string> DoUploadFileAsync(string fileName, byte[] fileData, bool isRechristen = true, string fileExtension = "")
  36. {
  37. if (_autoSliceSizeForUpload <= 0)
  38. {
  39. _autoSliceSizeForUpload = EnvironmentConfigManager.GetParammeter<IntParameter>("Storage", "AutoSliceSizeForUpload").Value * 2;
  40. if (_autoSliceSizeForUpload <= 0)
  41. {
  42. _autoSliceSizeForUpload = 5242880 * 2;
  43. }
  44. }
  45. if (fileData.Length > _autoSliceSizeForUpload)
  46. {
  47. var res = await AutoMultiPartUploadFileAsync(fileName, fileData, isRechristen, fileExtension);
  48. if (res != null)
  49. {
  50. return res.Item1;
  51. }
  52. else
  53. {
  54. return "";
  55. }
  56. }
  57. else
  58. {
  59. var resultTuple = await MultiPartUploadAsync(fileName, fileData, isRechristen, fileExtension);
  60. if (resultTuple == null)
  61. {
  62. Logger.WriteLineInfo("Do MultiPartUploadAsync Fail");
  63. return "";
  64. }
  65. var resultUrl = resultTuple.Item1;
  66. return resultUrl;
  67. }
  68. }
  69. // <summary>
  70. /// 上传文件, 自动分块并最终合并文件
  71. /// </summary>
  72. /// <param name="record"></param>
  73. /// <param name="dic"></param>
  74. /// <param name="isRechristen"></param>
  75. /// <returns></returns>
  76. public async Task<string> ManualMultiPartUploadFileAsync(FileTransferRecorder record, Dictionary<int, byte[]> dic, bool isRechristen = true)
  77. {
  78. if (_autoSliceSizeForUpload <= 0)
  79. {
  80. _autoSliceSizeForUpload = EnvironmentConfigManager.GetParammeter<IntParameter>("Storage", "AutoSliceSizeForUpload").Value * 2;
  81. if (_autoSliceSizeForUpload <= 0)
  82. {
  83. _autoSliceSizeForUpload = 5242880 * 2;
  84. }
  85. }
  86. string fileName = record.ObjectName + record.FileExtension;
  87. var tupleInitResult = await DoInitMulitPartUploadFileAsync(fileName);
  88. if (tupleInitResult == null)
  89. {
  90. Logger.WriteLineInfo("ManualMultiPartUploadFileAsync Do InitMulitPartUploadFile Fail");
  91. return "";
  92. }
  93. var fileUrl = tupleInitResult.Item1;
  94. if (string.IsNullOrEmpty(tupleInitResult.Item2))
  95. {
  96. Logger.WriteLineInfo("ManualMultiPartUploadFileAsync Get Response InitMulitPartUploadFile Fail");
  97. return "";
  98. }
  99. byte[] bytesR = System.Text.Encoding.UTF8.GetBytes(tupleInitResult.Item2.Replace("\\\"","'").Replace("\\r","").Replace("\\n", "").Replace("utf-16", "utf-8"));
  100. var initResult = SignatureHelper.DeserializeXML<InitiateMultipartUploadResult>(bytesR) ?? new InitiateMultipartUploadResult();
  101. //分块上传
  102. var dicPart = new Dictionary<int, byte[]>();
  103. int allIndex = 0;
  104. int allPartCount = 0;
  105. foreach (var partDetail in record.PartList)
  106. {
  107. var partCount = (int)((dic[partDetail.PartNumber].Length / _autoSliceSizeForUpload) + (dic[partDetail.PartNumber].Length % _autoSliceSizeForUpload > 0 ? 1 : 0));
  108. if (partDetail.PartNumber == 0)
  109. {
  110. allPartCount = partCount;
  111. }
  112. var offsetIndex = 0;
  113. for (var i = 0; i < partCount; i++)
  114. {
  115. var tempLength = _autoSliceSizeForUpload;
  116. if ((partCount - 1) == i)
  117. {
  118. //最后一个分片
  119. tempLength = dic[partDetail.PartNumber].Length - (partCount - 1) * _autoSliceSizeForUpload;
  120. }
  121. var tempByte = new byte[tempLength];
  122. offsetIndex = i * _autoSliceSizeForUpload;
  123. Array.Copy(dic[partDetail.PartNumber], offsetIndex, tempByte, 0, tempByte.Length);
  124. allIndex = allPartCount * partDetail.PartNumber + i + 1;
  125. dicPart.Add(allIndex, tempByte);
  126. }
  127. }
  128. var dicKeys = dicPart.Keys.OrderBy(c => c).ToList();
  129. //开始分块上传
  130. var parallelOption = new ParallelOptions() { MaxDegreeOfParallelism = 3 };
  131. var resultDic = new ConcurrentDictionary<int, PartInfo>();
  132. Parallel.ForEach(dicKeys, parallelOption, item => {
  133. var requestParams = new List<KeyValuePair<string, string>>();
  134. requestParams.Add(
  135. new KeyValuePair<string, string>("partNumber", item.ToString())
  136. );
  137. requestParams.Add(
  138. new KeyValuePair<string, string>("uploadId", initResult.UploadId)
  139. );
  140. var multiResult = MultiPartUploadAsync(fileName, dicPart[item], isRechristen, record.FileExtension, requestParams).GetAwaiter().GetResult();
  141. if (multiResult != null)
  142. {
  143. var tempEntity = new PartInfo()
  144. {
  145. PartNumber = item,
  146. ETag = multiResult.Item2
  147. };
  148. resultDic.TryAdd(item, tempEntity);
  149. }
  150. });
  151. //结束上传
  152. if (resultDic.Count == dicKeys.Count)
  153. {
  154. //上传成功
  155. var completeEntity = new CompleteMultipartUpload();
  156. foreach (var dicItem in dicKeys)
  157. {
  158. completeEntity.Part.Add(resultDic[dicItem]);
  159. }
  160. var requestParams = new List<KeyValuePair<string, string>>();
  161. requestParams.Add(
  162. new KeyValuePair<string, string>("uploadId", initResult.UploadId)
  163. );
  164. var tupleResult = await DoCompleteMulitPartUploadFileAsync(fileName, completeEntity, isRechristen, requestParams);
  165. if (tupleResult == null || string.IsNullOrEmpty(tupleResult.Item2))
  166. {
  167. Logger.WriteLineInfo("ManualMultiPartUploadFileAsync Do CompleteMulitPartUploadFileAsync Fail");
  168. return null;
  169. }
  170. foreach (var partDetail in record.PartList)
  171. {
  172. partDetail.IsUploadComplete = true;
  173. }
  174. record.Host = tupleResult.Item1.Replace(fileName, "");
  175. record.IsUploadComplete = true;
  176. return tupleResult.Item1;
  177. }
  178. else
  179. {
  180. Logger.WriteLineInfo("ManualMultiPartUploadFileAsync Do MultiPartUploadAsync Complete Fail");
  181. return null;
  182. }
  183. }
  184. /// <summary>
  185. /// 自动分块上传,并在服务端自动合并
  186. /// </summary>
  187. private async Task<Tuple<string, string>> AutoMultiPartUploadFileAsync(string fileName, byte[] fileData, bool isRechristen = true, string fileExtension = "")
  188. {
  189. //自动分段上传
  190. //1.初始化
  191. var tupleInitResult = await DoInitMulitPartUploadFileAsync(fileName);
  192. if (tupleInitResult == null)
  193. {
  194. Logger.WriteLineInfo("Do InitMulitPartUploadFile Fail");
  195. return null;
  196. }
  197. var fileUrl = tupleInitResult.Item1;
  198. if (string.IsNullOrEmpty(tupleInitResult.Item2))
  199. {
  200. Logger.WriteLineInfo("Get Response InitMulitPartUploadFile Fail");
  201. return null;
  202. }
  203. byte[] bytesR = System.Text.Encoding.UTF8.GetBytes(tupleInitResult.Item2.Replace("\\\"","'").Replace("\\r","").Replace("\\n", "").Replace("utf-16", "utf-8"));
  204. var initResult = SignatureHelper.DeserializeXML<InitiateMultipartUploadResult>(bytesR) ?? new InitiateMultipartUploadResult();
  205. if (string.IsNullOrWhiteSpace(initResult.UploadId) && !string.IsNullOrWhiteSpace(tupleInitResult.Item2))
  206. {
  207. initResult = SignatureHelper.DeserializeXML<InitiateMultipartUploadResult>(tupleInitResult.Item2.Replace("\\\"","'").Replace("\\r","").Replace("\\n", "").Replace("utf-16", "utf-8")) ?? new InitiateMultipartUploadResult();
  208. }
  209. //分块上传
  210. var dic = new Dictionary<int, byte[]>();
  211. var partCount = (int)((fileData.Length / _autoSliceSizeForUpload) + (fileData.Length % _autoSliceSizeForUpload > 0 ? 1 : 0));
  212. var offsetIndex = 0;
  213. for (var i = 0; i < partCount; i++)
  214. {
  215. var tempLength = _autoSliceSizeForUpload;
  216. if ((partCount - 1) == i)
  217. {
  218. //最后一个分片
  219. tempLength = fileData.Length - (partCount - 1) * _autoSliceSizeForUpload;
  220. }
  221. var tempByte = new byte[tempLength];
  222. offsetIndex = i * _autoSliceSizeForUpload;
  223. Array.Copy(fileData, offsetIndex, tempByte, 0, tempByte.Length);
  224. dic.Add((i + 1), tempByte);
  225. }
  226. var dicKeys = dic.Keys.OrderBy(c => c).ToList();
  227. //开始分块上传
  228. var parallelOption = new ParallelOptions() { MaxDegreeOfParallelism = 3 };
  229. var resultDic = new ConcurrentDictionary<int, PartInfo>();
  230. Parallel.ForEach(dicKeys, parallelOption, item => {
  231. var requestParams = new List<KeyValuePair<string, string>>();
  232. requestParams.Add(
  233. new KeyValuePair<string, string>("partNumber", item.ToString())
  234. );
  235. requestParams.Add(
  236. new KeyValuePair<string, string>("uploadId", initResult.UploadId)
  237. );
  238. var multiResult = MultiPartUploadAsync(fileName, dic[item], isRechristen, fileExtension, requestParams).GetAwaiter().GetResult();
  239. if (multiResult != null)
  240. {
  241. var tempEntity = new PartInfo()
  242. {
  243. PartNumber = item,
  244. ETag = multiResult.Item2
  245. };
  246. resultDic.TryAdd(item, tempEntity);
  247. }
  248. });
  249. //结束上传
  250. if (resultDic.Count == dicKeys.Count)
  251. {
  252. //上传成功
  253. var completeEntity = new CompleteMultipartUpload();
  254. foreach (var dicItem in dicKeys)
  255. {
  256. completeEntity.Part.Add(resultDic[dicItem]);
  257. }
  258. var requestParams = new List<KeyValuePair<string, string>>();
  259. requestParams.Add(
  260. new KeyValuePair<string, string>("uploadId", initResult.UploadId)
  261. );
  262. var tupleResult = await DoCompleteMulitPartUploadFileAsync(fileName, completeEntity, isRechristen, requestParams);
  263. if (tupleResult == null || string.IsNullOrEmpty(tupleInitResult.Item2))
  264. {
  265. Logger.WriteLineInfo("Do CompleteMulitPartUploadFileAsync Fail");
  266. return null;
  267. }
  268. return tupleResult;
  269. }
  270. else
  271. {
  272. Logger.WriteLineInfo("Do MultiPartUploadAsync Complete Fail");
  273. return null;
  274. }
  275. }
  276. /// <summary>
  277. /// 分块上传
  278. /// </summary>
  279. /// <param name="fileData"></param>
  280. /// <returns>上传成功的url</returns>
  281. private async Task<Tuple<string, string>> MultiPartUploadAsync(string fileName, byte[] fileData, bool isRechristen = true, string fileExtension = "", List<KeyValuePair<string, string>> requestParams = null)
  282. {
  283. var requestHeads = new Dictionary<string, string>();
  284. var authTuple = SignatureHelper.GetAuthorizationAsync(fileName, isRechristen, "put", requestParams);
  285. if (authTuple == null)
  286. {
  287. Logger.WriteLineInfo("Create Signature Fail");
  288. return null;
  289. }
  290. requestHeads.Add("Authorization", authTuple.Item1);
  291. var fileUrl = authTuple.Item2;
  292. var eTag = "";
  293. if (requestParams?.Count > 0)
  294. {
  295. var stringParamBuilder = new StringBuilder();
  296. requestParams = requestParams.OrderBy(c => c.Key).ToList();//TODO check
  297. foreach (var param in requestParams)
  298. {
  299. var encodeKey = SignatureHelper.Encode(param.Key);
  300. var encodeValue = SignatureHelper.Encode(param.Value);
  301. stringParamBuilder.Append($"{encodeKey}={encodeValue}&");
  302. }
  303. var httpParameters = stringParamBuilder.ToString();
  304. if (!string.IsNullOrEmpty(httpParameters))
  305. {
  306. httpParameters = httpParameters.TrimEnd('&');
  307. fileUrl += "?" + httpParameters;
  308. }
  309. }
  310. using (var request = new HttpRequestMessage())
  311. {
  312. var mimeType = FileHelper.GetMimeType(fileExtension);
  313. var contentType = MediaTypeHeaderValue.Parse("application/octet-stream");
  314. using (var content = new HttpUploadContent(fileData, contentType))
  315. {
  316. request.RequestUri = new Uri(fileUrl);
  317. request.Method = HttpMethod.Put;
  318. request.Content = content;
  319. foreach (var head in requestHeads)
  320. {
  321. request.Headers.TryAddWithoutValidation(head.Key, head.Value);
  322. }
  323. var result = await ExecuteRequestAsync(request);
  324. if (result == null || !result.Item1 || string.IsNullOrEmpty(result.Item2))
  325. {
  326. throw new Exception("Upload file failed!");
  327. }
  328. eTag = result.Item2;
  329. }
  330. }
  331. return Tuple.Create<string, string>(fileUrl, eTag);
  332. }
  333. /// <summary>
  334. /// 执行copyfile 操作
  335. /// </summary>
  336. /// <param name="sourceFileUrl">源目标文件</param>
  337. /// <param name="fileName"></param>
  338. /// <returns></returns>
  339. public async Task<List<string>> DoCopyFileAsync(string sourceFileUrl, string fileName, string destinationServer = "")
  340. {
  341. var resList = new List<string>();
  342. if (_otherCloudStorageNodes.Count <= 0)
  343. {
  344. var otherNodes = EnvironmentConfigManager.GetParammeter<StringParameter>("Storage", "OtherNodes").Value;
  345. var tempList = SignatureHelper.GetOtherCloudStorageNodeList(otherNodes);
  346. if (tempList?.Count > 0)
  347. {
  348. _otherCloudStorageNodes = tempList;
  349. }
  350. }
  351. if (_otherCloudStorageNodes.Count <= 0)
  352. {
  353. Logger.WriteLineInfo("Other Nodes Config Info Error");
  354. return null;
  355. }
  356. //处理原链接
  357. var newSourceFileUrl = sourceFileUrl.Replace("https://", "").Replace("http://", "");
  358. //处理copy节点
  359. var bucketList = new List<string>();
  360. if (string.IsNullOrEmpty(destinationServer))
  361. {
  362. bucketList = _otherCloudStorageNodes;
  363. }
  364. else
  365. {
  366. bucketList = _otherCloudStorageNodes.FindAll(c => c.Contains(destinationServer));
  367. }
  368. if (bucketList?.Count > 0)
  369. {
  370. var storageServer = ConfigurationManager.GetParammeter<StringParameter>("Storage", "StorageServer").Value;
  371. var serverType = storageServer == "VCS" ? StorageServerEnum.Vinno : StorageServerEnum.Tencent;
  372. foreach (var destinationBucket in bucketList)
  373. {
  374. if (sourceFileUrl.Contains(destinationBucket))
  375. {
  376. continue;
  377. }
  378. //生成签名
  379. List<KeyValuePair<string, string>> _headers = new List<KeyValuePair<string, string>>()
  380. {
  381. new KeyValuePair<string, string>("x-cos-copy-source", newSourceFileUrl),
  382. new KeyValuePair<string, string>("host", destinationBucket),
  383. };
  384. var authorization = SignatureHelper.GetStorageCopyAuthorization(fileName, serverType, _headers);
  385. var requestHeads = new Dictionary<string, string>();
  386. requestHeads.Add("Authorization", authorization);
  387. var destinationUrl = "https://" + destinationBucket + "/" + fileName;
  388. var result = await CopyFileAsync(destinationUrl, newSourceFileUrl, requestHeads);
  389. if (result == true)
  390. {
  391. resList.Add(destinationUrl);
  392. }
  393. }
  394. }
  395. return resList;
  396. }
  397. /// <summary>
  398. /// 复制文件
  399. /// </summary>
  400. /// <param name="destinationUrl">目标服务器</param>
  401. /// <param name="sourceFileUrl">源服务器</param>
  402. /// <param name="heads">签名</param>
  403. /// <returns>是否成功</returns>
  404. public async Task<bool> CopyFileAsync(string destinationUrl,string sourceFileUrl, Dictionary<string, string> heads)
  405. {
  406. var res = false;
  407. using (var request = new HttpRequestMessage())
  408. {
  409. try
  410. {
  411. request.RequestUri = new Uri(destinationUrl);
  412. request.Method = HttpMethod.Put;
  413. request.Headers.TryAddWithoutValidation("x-cos-copy-source", sourceFileUrl);
  414. foreach (var head in heads)
  415. {
  416. request.Headers.TryAddWithoutValidation(head.Key, head.Value);
  417. }
  418. var result = await ExecuteRequestAsync(request);
  419. if (result == null || !result.Item1)
  420. {
  421. Logger.WriteLineError($"FileTransfer CopyFileAsync fail, destinationUrl: {destinationUrl}, sourceFileUrl: {sourceFileUrl}");
  422. return false;
  423. }
  424. res = result.Item1;
  425. }
  426. catch(Exception ex)
  427. {
  428. Logger.WriteLineError($"FileTransfer CopyFileAsync error, destinationUrl: {destinationUrl}, sourceFileUrl: {sourceFileUrl}, ex: {ex}");
  429. }
  430. }
  431. return res;
  432. }
  433. /// <summary>
  434. /// 执行请求
  435. /// </summary>
  436. /// <param name="httpRequestMessage"></param>
  437. /// <typeparam name="T"></typeparam>
  438. /// <returns></returns>
  439. private async Task<Tuple<bool, string>> ExecuteRequestAsync(HttpRequestMessage httpRequestMessage)
  440. {
  441. try
  442. {
  443. var response = await _httpClient.SendAsync(httpRequestMessage);
  444. bool res = false;
  445. var eTag = "";
  446. if (response != null && response.StatusCode == HttpStatusCode.OK)
  447. {
  448. eTag = response.Headers?.ETag?.Tag ?? string.Empty;
  449. res = true;
  450. var resultContent = response.Content.Headers.ContentLength > 0;
  451. if (resultContent)
  452. {
  453. eTag = response.Content.Headers.ContentLength.ToString();
  454. }
  455. }
  456. return Tuple.Create<bool, string>(res, eTag);
  457. }
  458. catch (Exception ex)
  459. {
  460. throw ex;
  461. }
  462. }
  463. /// <summary>
  464. /// 初始化分块上传文件
  465. /// </summary>
  466. /// <param name="fileName"></param>
  467. /// <param name="fileData"></param>
  468. /// <returns></returns>
  469. public async Task<Tuple<string, string>> DoInitMulitPartUploadFileAsync(string fileName)
  470. {
  471. var requestHeads = new Dictionary<string, string>();
  472. var authTuple = SignatureHelper.GetAuthorizationAsync(fileName, false, "post");
  473. if (authTuple == null)
  474. {
  475. Logger.WriteLineInfo("Create Signature Fail");
  476. return null;
  477. }
  478. requestHeads.Add("Authorization", authTuple.Item1);
  479. var fileUrl = authTuple.Item2 + "?uploads";
  480. using (var request = new HttpRequestMessage())
  481. {
  482. request.RequestUri = new Uri(fileUrl);
  483. request.Method = HttpMethod.Post;
  484. foreach (var head in requestHeads)
  485. {
  486. request.Headers.TryAddWithoutValidation(head.Key, head.Value);
  487. }
  488. request.Headers.TryAddWithoutValidation("Content-Type","application/x-zip-compressed");
  489. request.Headers.TryAddWithoutValidation("Content-Length", "0");
  490. var response = await _httpClient.SendAsync(request);
  491. if (response != null && response.StatusCode == HttpStatusCode.OK)
  492. {
  493. //read response content
  494. var result = await response.Content.ReadAsStringAsync();
  495. return Tuple.Create<string, string>(fileUrl, result);
  496. }
  497. else
  498. {
  499. throw new Exception("Upload file failed!");
  500. }
  501. }
  502. }
  503. /// <summary>
  504. /// 初始化分块上传文件
  505. /// </summary>
  506. /// <param name="fileName"></param>
  507. /// <param name="fileData"></param>
  508. /// <returns></returns>
  509. public async Task<Tuple<string, string>> DoCompleteMulitPartUploadFileAsync(string fileName, CompleteMultipartUpload xmlUpload, bool isRechristen = true, List<KeyValuePair<string, string>> requestParams = null)
  510. {
  511. var requestHeads = new Dictionary<string, string>();
  512. var mimeType = FileHelper.GetMimeType(".config");
  513. List<KeyValuePair<string, string>> headers = new List<KeyValuePair<string, string>>();
  514. headers.Add(
  515. new KeyValuePair<string, string>("Content-Type", mimeType)
  516. );
  517. var authTuple = SignatureHelper.GetAuthorizationAsync(fileName, isRechristen, "post", requestParams, headers, true);
  518. if (authTuple == null)
  519. {
  520. Logger.WriteLineInfo("Create Signature Fail");
  521. return null;
  522. }
  523. requestHeads.Add("Authorization", authTuple.Item1);
  524. var fileUrl = authTuple.Item2;
  525. if (requestParams?.Count > 0)
  526. {
  527. var stringParamBuilder = new StringBuilder();
  528. requestParams = requestParams.OrderBy(c => c.Key).ToList();//TODO check
  529. foreach (var param in requestParams)
  530. {
  531. var encodeKey = SignatureHelper.Encode(param.Key);
  532. var encodeValue = SignatureHelper.Encode(param.Value);
  533. stringParamBuilder.Append($"{encodeKey}={encodeValue}&");
  534. }
  535. var httpParameters = stringParamBuilder.ToString();
  536. if (!string.IsNullOrEmpty(httpParameters))
  537. {
  538. httpParameters = httpParameters.TrimEnd('&');
  539. fileUrl += "?" + httpParameters;
  540. }
  541. }
  542. var xml = "";
  543. if (xmlUpload.Part?.Count > 0)
  544. {
  545. xml = "<CompleteMultipartUpload>";
  546. foreach (var partInfo in xmlUpload.Part)
  547. {
  548. xml += "<Part>";
  549. xml += "<PartNumber>" + partInfo.PartNumber + "</PartNumber>";
  550. xml += "<ETag>" + partInfo.ETag + "</ETag>";
  551. xml += "</Part>";
  552. }
  553. xml += "</CompleteMultipartUpload>";
  554. }
  555. if (!string.IsNullOrWhiteSpace(xml)) {
  556. byte[] bytesR = System.Text.Encoding.UTF8.GetBytes(xml);
  557. var byteContent = new ByteArrayContent(bytesR);
  558. byteContent.Headers.ContentType = MediaTypeHeaderValue.Parse(mimeType);
  559. foreach (var head in requestHeads)
  560. {
  561. _httpClient.DefaultRequestHeaders.TryAddWithoutValidation(head.Key, head.Value);
  562. }
  563. var response = await _httpClient.PostAsync(fileUrl, byteContent).ConfigureAwait(false);
  564. if (response != null && response.StatusCode == HttpStatusCode.OK)
  565. {
  566. //read response content
  567. var result = await response.Content.ReadAsStringAsync();
  568. return Tuple.Create<string, string>(authTuple.Item2, result);
  569. }
  570. else
  571. {
  572. Logger.WriteLineWarn($"UploadFile DoCompleteMulitPartUploadFileAsync failed, response:{response}");
  573. throw new Exception("Upload file failed!");
  574. }
  575. }
  576. else
  577. {
  578. throw new Exception("Upload Xml Data failed!");
  579. }
  580. }
  581. /// <summary>
  582. /// 下载http请求
  583. /// </summary>
  584. /// <param name="requestUrl"></param>
  585. /// <param name="bytes"></param>
  586. /// <param name="rangeFrom"></param>
  587. /// <param name="rangeTo"></param>
  588. /// <param name="fileSize"></param>
  589. /// <returns>下载是否成功</returns>
  590. public async Task<bool> DoHttpRequest(string requestUrl, byte[] bytes, long rangeFrom, long rangeTo, long fileSize)
  591. {
  592. try
  593. {
  594. using (var downloadRequest = new HttpRequestMessage())
  595. {
  596. downloadRequest.RequestUri = new Uri(requestUrl);
  597. downloadRequest.Method = HttpMethod.Get;
  598. if (rangeTo > fileSize)
  599. {
  600. rangeTo = fileSize;
  601. }
  602. downloadRequest.Headers.Range = new RangeHeaderValue(rangeFrom, rangeTo);
  603. var response = await _httpClient.SendAsync(downloadRequest);
  604. using (var downloadStream = await GetStreamResult(response))
  605. {
  606. if (downloadStream == null)
  607. {
  608. return false;
  609. }
  610. var readLength = rangeTo - rangeFrom;
  611. var dataToWrite = new byte[readLength];
  612. var downloadLength = downloadStream.Read(dataToWrite, 0, (int)readLength);
  613. Array.Copy(dataToWrite, 0, bytes, rangeFrom, downloadLength);
  614. }
  615. }
  616. }
  617. catch (Exception ex)
  618. {
  619. Logger.WriteLineError($"Download fail, {requestUrl}, {ex}");
  620. return false;
  621. }
  622. GC.Collect();
  623. return true;
  624. }
  625. /// <summary>
  626. /// 获取文件流
  627. /// </summary>
  628. /// <param name="response"></param>
  629. /// <returns></returns>
  630. private async Task<Stream> GetStreamResult(HttpResponseMessage response)
  631. {
  632. if (response != null && (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.PartialContent))
  633. {
  634. return await response.Content.ReadAsStreamAsync();
  635. }
  636. if (response != null && response.StatusCode == HttpStatusCode.Forbidden)
  637. {
  638. var resultCode = await response.Content.ReadAsStringAsync();
  639. if (resultCode.IndexOf("-148671") > -1)
  640. {
  641. var fileUrl = response.RequestMessage?.RequestUri?.ToString() ?? string.Empty;
  642. var request = new HttpRequestMessage();
  643. request.RequestUri = new Uri($"{fileUrl}?restore");
  644. request.Method = HttpMethod.Put;
  645. var responseTask = _httpClient.SendAsync(request);
  646. responseTask.GetAwaiter().GetResult();
  647. }
  648. }
  649. return null;
  650. }
  651. }
  652. }