ObjectStorage.cs 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. using System;
  2. using System.Collections.Concurrent;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Net.Http;
  6. using System.Net.Http.Headers;
  7. using Vinno.vCloud.Common.Storage.ObjectStorageInfo.Solid;
  8. using Vinno.IUS.Common.Log;
  9. using System.Threading;
  10. namespace Vinno.vCloud.Common.Storage.ObjectStorageInfo
  11. {
  12. public class ObjectStorage : IObjectStorage
  13. {
  14. public ObjectStorage(ObjectUserAgent objectUserAgent)
  15. {
  16. _objectUserAgent = objectUserAgent;
  17. }
  18. private ObjectUserAgent _objectUserAgent = null;
  19. private const int chunkSize = 2097152;//2M;
  20. /// <summary>
  21. /// 上传文件(字节流)
  22. /// </summary>
  23. /// <param name="filePath"></param>
  24. /// <param name="fileName"></param>
  25. /// <param name="progressCallback"></param>
  26. /// <returns></returns>
  27. public bool UploadFile(byte[] fileData, MediaTypeHeaderValue mediaTypeHeaderValue, string requestUrl, Dictionary<string, string> heads, ProductInfoHeaderValue productInfoHeader, Action<double> progressCallback = null, CancellationTokenSource cancelTokenSource = null)
  28. {
  29. if (fileData == null || fileData.Length == 0)
  30. {
  31. throw new Exception("FileData is empty");
  32. }
  33. if (_objectUserAgent == null)
  34. {
  35. throw new Exception("UFileConfig not set");
  36. }
  37. long allStreamLength = 0;
  38. using (var requestHead = new HttpRequestMessage())
  39. {
  40. try
  41. {
  42. requestHead.RequestUri = new Uri(requestUrl);
  43. requestHead.Method = HttpMethod.Head;
  44. allStreamLength = HttpClientHelper.ExecuteRequest(requestHead, HttpClientHelper.GetLengthResult,false, cancelTokenSource);
  45. if (fileData.Length == allStreamLength)
  46. {
  47. return true;
  48. }
  49. }
  50. catch (Exception ex)
  51. {
  52. Logger.WriteLineError($"Get file head error {ex}");
  53. }
  54. }
  55. using (var request = new HttpRequestMessage())
  56. {
  57. using (var content = new UploadContent(fileData, mediaTypeHeaderValue))
  58. {
  59. content.ProgressChanged += (sender, progress) => { progressCallback?.Invoke(progress); };
  60. request.RequestUri = new Uri(requestUrl);
  61. request.Method = HttpMethod.Put;
  62. request.Content = content;
  63. if (productInfoHeader != null)
  64. {
  65. request.Headers.UserAgent.Add(productInfoHeader);
  66. }
  67. foreach (var head in heads)
  68. {
  69. request.Headers.TryAddWithoutValidation(head.Key, head.Value);
  70. }
  71. return HttpClientHelper.ExecuteRequest(request, HttpClientHelper.GetBooleanResult,false,cancelTokenSource);
  72. }
  73. }
  74. }
  75. /// <summary>
  76. /// 复制文件
  77. /// </summary>
  78. /// <param name="destinationUrl">目标服务器</param>
  79. /// <param name="sourceFileUrl">源服务器</param>
  80. /// <param name="heads">签名</param>
  81. /// <returns>是否成功</returns>
  82. public bool CopyFile(string destinationUrl,string sourceFileUrl, Dictionary<string, string> heads)
  83. {
  84. var res = false;
  85. using (var request = new HttpRequestMessage())
  86. {
  87. request.RequestUri = new Uri(destinationUrl);
  88. request.Method = HttpMethod.Put;
  89. request.Headers.TryAddWithoutValidation("x-cos-copy-source", sourceFileUrl);
  90. foreach (var head in heads)
  91. {
  92. request.Headers.TryAddWithoutValidation(head.Key, head.Value);
  93. }
  94. res = HttpClientHelper.ExecuteRequest(request);
  95. }
  96. return res;
  97. }
  98. /// <summary>
  99. /// 上传文件
  100. /// </summary>
  101. /// <param name="filePath"></param>
  102. /// <param name="fileName"></param>
  103. /// <param name="progressCallback"></param>
  104. /// <returns></returns>
  105. public bool UploadFile(string filePath, string mimeType, string requestUrl, Dictionary<string, string> heads, ProductInfoHeaderValue productInfoHeader, Action<double> progressCallback = null)
  106. {
  107. if (string.IsNullOrWhiteSpace(filePath))
  108. {
  109. Logger.WriteLineError("filePath can't be empty");
  110. return false;
  111. }
  112. if (string.IsNullOrWhiteSpace(requestUrl))
  113. {
  114. Logger.WriteLineError("requestUrl can't be empty");
  115. return false;
  116. }
  117. if (_objectUserAgent == null)
  118. {
  119. throw new Exception("UFileConfig not set");
  120. }
  121. long allStreamLength = 0;
  122. using (var requestHead = new HttpRequestMessage())
  123. {
  124. try
  125. {
  126. requestHead.RequestUri = new Uri(requestUrl);
  127. requestHead.Method = HttpMethod.Head;
  128. allStreamLength = HttpClientHelper.ExecuteRequest(requestHead, HttpClientHelper.GetLengthResult);
  129. }
  130. catch (Exception ex)
  131. {
  132. Logger.WriteLineError($"Get file head error {ex}");
  133. }
  134. }
  135. using (var request = new HttpRequestMessage())
  136. {
  137. using (var content = new UploadContent(filePath))
  138. {
  139. try
  140. {
  141. if (content.CompareFileSizes(allStreamLength))
  142. {
  143. return true;
  144. }
  145. content.ProgressChanged += (sender, progress) => { progressCallback?.Invoke(progress); };
  146. content.Headers.ContentType = MediaTypeHeaderValue.Parse(mimeType);
  147. request.RequestUri = new Uri(requestUrl);
  148. request.Method = HttpMethod.Put;
  149. request.Content = content;
  150. if (productInfoHeader != null)
  151. {
  152. request.Headers.UserAgent.Add(productInfoHeader);
  153. }
  154. foreach (var head in heads)
  155. {
  156. request.Headers.TryAddWithoutValidation(head.Key, head.Value);
  157. }
  158. return HttpClientHelper.ExecuteRequest(request, HttpClientHelper.GetBooleanResult);
  159. }
  160. catch (Exception ex)
  161. {
  162. Logger.WriteLineError($"{ex}");
  163. return false;
  164. }
  165. finally
  166. {
  167. content.Dispose();
  168. }
  169. }
  170. }
  171. }
  172. /// <summary>
  173. /// 批量上传文件
  174. /// </summary>
  175. /// <param name="uFileInfos"></param>
  176. /// <param name="progressCallback"></param>
  177. /// <returns></returns>
  178. public bool UploadFiles(List<ObjectFileInfo> uFileInfos, Dictionary<string, string> heads, ProductInfoHeaderValue productInfoHeader)
  179. {
  180. if (_objectUserAgent == null)
  181. {
  182. throw new Exception("UFileConfig not set");
  183. }
  184. ConcurrentQueue<QueueRequestEntity> httpRequestMessageQueue = new ConcurrentQueue<QueueRequestEntity>();
  185. int queueIndex = 1;
  186. foreach (var file in uFileInfos)
  187. {
  188. if (file != null && File.Exists(file.FilePath))
  189. {
  190. long allStreamLength = 0;
  191. using (var requestHead = new HttpRequestMessage())
  192. {
  193. try
  194. {
  195. requestHead.RequestUri = new Uri(file.RequestUrl);
  196. requestHead.Method = HttpMethod.Head;
  197. allStreamLength = HttpClientHelper.ExecuteRequest(requestHead, HttpClientHelper.GetLengthResult);
  198. file.FileLength = allStreamLength;
  199. }
  200. catch (Exception ex)
  201. {
  202. Logger.WriteLineError($"Get file head error {ex}");
  203. }
  204. }
  205. var request = new HttpRequestMessage();
  206. request.RequestUri = new Uri(file.RequestUrl);
  207. request.Method = HttpMethod.Put;
  208. if (productInfoHeader != null)
  209. {
  210. request.Headers.UserAgent.Add(productInfoHeader);
  211. }
  212. foreach (var head in heads)
  213. {
  214. request.Headers.TryAddWithoutValidation(head.Key, head.Value);
  215. }
  216. request.Headers.Add("Authorization", file.Authorization);
  217. httpRequestMessageQueue.Enqueue(new QueueRequestEntity
  218. {
  219. FileInfo = file,
  220. QueueCount = uFileInfos.Count,
  221. QueueIndex = queueIndex,
  222. Request = request,
  223. CompleteWorker = UploadFileCallback
  224. });
  225. queueIndex++;
  226. }
  227. }
  228. var result = HttpClientHelper.ExecuteRequestQueue(httpRequestMessageQueue);
  229. return result;
  230. }
  231. /// <summary>
  232. /// 上传文件回调
  233. /// </summary>
  234. /// <param name="queueWorkItem"></param>
  235. private void UploadFileCallback(QueueWorkItem queueWorkItem)
  236. {
  237. queueWorkItem.ProgressCallback?.Invoke((int)(100.0 * queueWorkItem.QueueIndex / queueWorkItem.QueueCount));
  238. }
  239. /// <summary>
  240. /// 获取文件
  241. /// </summary>
  242. /// <param name="requestUrl">请求地址</param>
  243. /// <param name="from">Range开始位置</param>
  244. /// <param name="to">Range结束位置</param>
  245. /// <param name="progressCallback"></param>
  246. /// <returns></returns>
  247. public byte[] GetFile(string requestUrl, long from, long to, Action<double> progressCallback = null)
  248. {
  249. if (string.IsNullOrWhiteSpace(requestUrl))
  250. {
  251. Logger.WriteLineError("requestUrl can't be empty");
  252. return new byte[0];
  253. }
  254. if (_objectUserAgent == null)
  255. {
  256. throw new Exception("UFileConfig not set");
  257. }
  258. using (var request = new HttpRequestMessage())
  259. {
  260. request.RequestUri = new Uri(requestUrl);
  261. request.Method = HttpMethod.Head;
  262. long allStreamLength = HttpClientHelper.ExecuteRequest(request, HttpClientHelper.GetLengthResult);
  263. using (var memoryStream = new MemoryStream())
  264. {
  265. if (Downloading(requestUrl, null, memoryStream, allStreamLength, from, to, true, progressCallback))
  266. {
  267. return memoryStream.ToArray();
  268. }
  269. return new byte[0];
  270. }
  271. }
  272. }
  273. /// <summary>
  274. /// 获取文件
  275. /// </summary>
  276. /// <param name="requestUrl"></param>
  277. /// <param name="progressCallback"></param>
  278. /// <returns></returns>
  279. public byte[] GetFile(string requestUrl, Action<double> progressCallback = null)
  280. {
  281. // Logger.WriteLineInfo($"ObjectStroage Get file buffer with file token:{requestUrl}");
  282. if (string.IsNullOrWhiteSpace(requestUrl))
  283. {
  284. Logger.WriteLineError("requestUrl can't be empty");
  285. return new byte[0];
  286. }
  287. if (_objectUserAgent == null)
  288. {
  289. throw new Exception("UFileConfig not set");
  290. }
  291. using (var request = new HttpRequestMessage())
  292. {
  293. request.RequestUri = new Uri(requestUrl);
  294. request.Method = HttpMethod.Head;
  295. long allStreamLength = HttpClientHelper.ExecuteRequest(request, HttpClientHelper.GetLengthResult);
  296. using (var memoryStream = new MemoryStream())
  297. {
  298. if (Downloading(requestUrl, null, memoryStream, allStreamLength, -1, -1, true, progressCallback))
  299. {
  300. // Logger.WriteLineInfo($"Downloading success with file token:{requestUrl}");
  301. return memoryStream.ToArray();
  302. }
  303. // Logger.WriteLineInfo($"Downloading done with file token:{requestUrl}");
  304. return new byte[0];
  305. }
  306. }
  307. }
  308. /// <summary>
  309. /// 下载文件
  310. /// </summary>
  311. /// <param name="filePath">文件路径</param>
  312. /// <param name="requestUrl">请求地址</param>
  313. /// <param name="from">Range开始位置</param>
  314. /// <param name="to">Range结束位置</param>
  315. /// <param name="progressCallback"></param>
  316. /// <returns></returns>
  317. public bool DownloadFile(string filePath, string requestUrl, long from, long to, Action<double> progressCallback = null)
  318. {
  319. if (string.IsNullOrWhiteSpace(filePath))
  320. {
  321. Logger.WriteLineError("filePath can't be empty");
  322. return false;
  323. }
  324. if (string.IsNullOrWhiteSpace(requestUrl))
  325. {
  326. Logger.WriteLineError("requestUrl can't be empty");
  327. return false;
  328. }
  329. if (_objectUserAgent == null)
  330. {
  331. throw new Exception("UFileConfig not set");
  332. }
  333. if (string.IsNullOrWhiteSpace(filePath))
  334. {
  335. throw new Exception("File path is empty");
  336. }
  337. using (var request = new HttpRequestMessage())
  338. {
  339. request.RequestUri = new Uri(requestUrl);
  340. request.Method = HttpMethod.Head;
  341. long allStreamLength = HttpClientHelper.ExecuteRequest(request, HttpClientHelper.GetLengthResult);
  342. Directory.CreateDirectory(Path.GetDirectoryName(filePath));
  343. FileMode fileMode = FileMode.CreateNew;
  344. if (File.Exists(filePath))
  345. {
  346. var fileInfo = new FileInfo(filePath);
  347. if ((DateTime.UtcNow - fileInfo.LastWriteTimeUtc).Hours <= 1)
  348. {
  349. progressCallback?.Invoke(1.0);
  350. return true;
  351. }
  352. else
  353. {
  354. File.Delete(filePath);
  355. }
  356. }
  357. var tempFile = filePath + ".tmp";
  358. if (File.Exists(tempFile))
  359. {
  360. var fileInfo = new FileInfo(tempFile);
  361. if ((DateTime.UtcNow - fileInfo.LastWriteTimeUtc).Hours <= 1)
  362. {
  363. fileMode = FileMode.OpenOrCreate;
  364. }
  365. else
  366. {
  367. File.Delete(tempFile);
  368. }
  369. }
  370. using (var fileStream = new FileStream(tempFile, fileMode, FileAccess.ReadWrite, FileShare.ReadWrite))
  371. {
  372. if (allStreamLength == 0)
  373. {
  374. return false;
  375. }
  376. if (fileStream.Length == allStreamLength)
  377. {
  378. progressCallback?.Invoke(100);
  379. return true;
  380. }
  381. return Downloading(requestUrl, filePath, fileStream, allStreamLength, from, to, true, progressCallback);
  382. }
  383. }
  384. }
  385. /// <summary>
  386. /// 下载文件
  387. /// </summary>
  388. /// <param name="fileName"></param>
  389. /// <param name="filePath"></param>
  390. /// <param name="progressCallback"></param>
  391. /// <returns></returns>
  392. public bool DownloadFile(string filePath, string requestUrl, Action<double> progressCallback = null,CancellationTokenSource cancelTokenSource = null)
  393. {
  394. if (string.IsNullOrWhiteSpace(filePath))
  395. {
  396. Logger.WriteLineError("filePath can't be empty");
  397. return false;
  398. }
  399. if (string.IsNullOrWhiteSpace(requestUrl))
  400. {
  401. Logger.WriteLineError("requestUrl can't be empty");
  402. return false;
  403. }
  404. if (_objectUserAgent == null)
  405. {
  406. throw new Exception("UFileConfig not set");
  407. }
  408. if (string.IsNullOrWhiteSpace(filePath))
  409. {
  410. throw new Exception("File path is empty");
  411. }
  412. using (var request = new HttpRequestMessage())
  413. {
  414. request.RequestUri = new Uri(requestUrl);
  415. request.Method = HttpMethod.Head;
  416. long allStreamLength = HttpClientHelper.ExecuteRequest(request, HttpClientHelper.GetLengthResult);
  417. Directory.CreateDirectory(Path.GetDirectoryName(filePath));
  418. FileMode fileMode = FileMode.CreateNew;
  419. if (File.Exists(filePath))
  420. {
  421. var fileInfo = new FileInfo(filePath);
  422. if (fileInfo.Length == allStreamLength && (DateTime.UtcNow - fileInfo.LastWriteTimeUtc).Hours <= 1)
  423. {
  424. progressCallback?.Invoke(1.0);
  425. return true;
  426. }
  427. else
  428. {
  429. File.Delete(filePath);
  430. }
  431. }
  432. var tempFile = filePath + ".tmp";
  433. if (File.Exists(tempFile))
  434. {
  435. Logger.WriteLineInfo($"Delete the temp file when the temp file {tempFile} already exist");
  436. File.Delete(tempFile);
  437. }
  438. if (allStreamLength == 0)
  439. {
  440. Logger.WriteLineWarn($"file length is empty, token:{requestUrl}");
  441. return false;
  442. }
  443. var result = false;
  444. using (var fileStream = new FileStream(tempFile, fileMode, FileAccess.ReadWrite, FileShare.ReadWrite))
  445. {
  446. result = Downloading(requestUrl, filePath, fileStream, allStreamLength, -1, -1, true, progressCallback, cancelTokenSource);
  447. if (!result)
  448. {
  449. fileStream.Dispose();
  450. }
  451. }
  452. if (!result && File.Exists(tempFile))
  453. {
  454. Logger.WriteLineInfo($"Delete the temp file when failed {tempFile}");
  455. File.Delete(tempFile);
  456. }
  457. return result;
  458. }
  459. }
  460. /// <summary>
  461. /// 批量下载文件
  462. /// </summary>
  463. /// <param name="uFileInfos"></param>
  464. /// <param name="progressCallback"></param>
  465. /// <returns></returns>
  466. public bool DownloadFiles(List<ObjectFileInfo> uFileInfos, Action<double> progressCallback = null)
  467. {
  468. if (_objectUserAgent == null)
  469. {
  470. throw new Exception("UFileConfig not set");
  471. }
  472. ConcurrentQueue<QueueRequestEntity> httpRequestMessageQueue = new ConcurrentQueue<QueueRequestEntity>();
  473. int queueIndex = 1;
  474. foreach (var file in uFileInfos)
  475. {
  476. var request = new HttpRequestMessage();
  477. request.RequestUri = new Uri(file.RequestUrl);
  478. request.Method = HttpMethod.Head;
  479. httpRequestMessageQueue.Enqueue(new QueueRequestEntity
  480. {
  481. FileInfo = file,
  482. QueueCount = uFileInfos.Count,
  483. QueueIndex = queueIndex,
  484. ProgressCallback = progressCallback,
  485. Request = request,
  486. CompleteWorker = DownloadFileCallback
  487. });
  488. queueIndex++;
  489. }
  490. var result = HttpClientHelper.ExecuteRequestQueue(httpRequestMessageQueue);
  491. return result;
  492. }
  493. /// <summary>
  494. /// 下载文件回调
  495. /// </summary>
  496. /// <param name="queueWorkItem"></param>
  497. private void DownloadFileCallback(QueueWorkItem queueWorkItem)
  498. {
  499. if (queueWorkItem.Response == null)
  500. {
  501. return;
  502. }
  503. long allStreamLength = HttpClientHelper.GetLengthResult(queueWorkItem.Response);
  504. var FileDirectory = Path.GetDirectoryName(queueWorkItem.FileInfo.FilePath);
  505. if (!Directory.Exists(FileDirectory))
  506. {
  507. Directory.CreateDirectory(FileDirectory);
  508. }
  509. FileMode fileMode = FileMode.CreateNew;
  510. if (File.Exists(queueWorkItem.FileInfo.FilePath))
  511. {
  512. var fileInfo = new FileInfo(queueWorkItem.FileInfo.FilePath);
  513. if (fileInfo.Length == allStreamLength && (DateTime.UtcNow - fileInfo.LastWriteTimeUtc).Hours <= 1)
  514. {
  515. queueWorkItem.ProgressCallback?.Invoke((int)(100.0 * queueWorkItem.QueueIndex / queueWorkItem.QueueCount));
  516. return;
  517. }
  518. else
  519. {
  520. File.Delete(queueWorkItem.FileInfo.FilePath);
  521. }
  522. }
  523. var tempFile = queueWorkItem.FileInfo.FilePath + ".tmp";
  524. if (File.Exists(tempFile))
  525. {
  526. var fileInfo = new FileInfo(tempFile);
  527. if ((DateTime.UtcNow - fileInfo.LastWriteTimeUtc).Hours <= 1)
  528. {
  529. fileMode = FileMode.OpenOrCreate;
  530. }
  531. else
  532. {
  533. File.Delete(tempFile);
  534. }
  535. }
  536. using (var fileStream = new FileStream(tempFile, fileMode, FileAccess.ReadWrite, FileShare.ReadWrite))
  537. {
  538. if (fileStream.Length == allStreamLength)
  539. {
  540. queueWorkItem.ProgressCallback?.Invoke((int)(100.0 * queueWorkItem.QueueIndex / queueWorkItem.QueueCount));
  541. return;
  542. }
  543. if (Downloading(queueWorkItem.FileInfo.RequestUrl, queueWorkItem.FileInfo.FilePath, fileStream, allStreamLength))
  544. {
  545. queueWorkItem.ProgressCallback?.Invoke((int)(100.0 * queueWorkItem.QueueIndex / queueWorkItem.QueueCount));
  546. return;
  547. }
  548. else
  549. {
  550. if (HttpClientHelper.IsFinalAgreement)
  551. {
  552. throw new Exception("Download fail");
  553. }
  554. }
  555. }
  556. }
  557. /// <summary>
  558. /// 下载逻辑
  559. /// </summary>
  560. /// <param name="requestUrl"></param>
  561. /// <param name="filePath"></param>
  562. /// <param name="fileStream"></param>
  563. /// <param name="fileSize"></param>
  564. /// <param name="from"></param>
  565. /// <param name="to"></param>
  566. /// <param name="isKeepAlive"></param>
  567. /// <param name="progressCallback"></param>
  568. /// <returns></returns>
  569. private bool Downloading(string requestUrl, string filePath, Stream fileStream, long fileSize, long from = -1, long to = -1, bool isKeepAlive = false, Action<double> progressCallback = null, CancellationTokenSource cancelTokenSource = null)
  570. {
  571. bool isDownloadSuccess = false;
  572. if (fileStream == null)
  573. {
  574. Logger.WriteLineError("fileStream can't be null");
  575. return false;
  576. }
  577. if (string.IsNullOrWhiteSpace(filePath) && !(fileStream is MemoryStream))
  578. {
  579. Logger.WriteLineError("filePath can't be empty");
  580. return false;
  581. }
  582. if (string.IsNullOrWhiteSpace(requestUrl))
  583. {
  584. Logger.WriteLineError("requestUrl can't be empty");
  585. return false;
  586. }
  587. try
  588. {
  589. if (from == -1 && to == -1)
  590. {
  591. fileStream.Seek(fileStream.Length, SeekOrigin.Current);
  592. for (int fileIndex = (int)fileStream.Length; true; fileIndex += chunkSize)
  593. {
  594. if (fileStream.Length >= fileSize)
  595. {
  596. break;
  597. }
  598. var newLength = GetNewLength(fileStream.Length, chunkSize, fileSize);
  599. if (!DoHttpRequest(requestUrl, fileStream, fileStream.Length, newLength, fileIndex, fileSize, isKeepAlive, progressCallback,cancelTokenSource))
  600. {
  601. if (cancelTokenSource != null && cancelTokenSource.IsCancellationRequested)
  602. {
  603. Logger.WriteLineError($"Download failed {filePath} with code position 1");
  604. return false;
  605. }
  606. break;
  607. }
  608. }
  609. }
  610. else
  611. {
  612. int tryCount = 0;
  613. do
  614. {
  615. if (!DoHttpRequest(requestUrl, fileStream, from, to, fileSize, fileSize, isKeepAlive, progressCallback,cancelTokenSource))
  616. {
  617. if (cancelTokenSource != null && cancelTokenSource.IsCancellationRequested)
  618. {
  619. Logger.WriteLineError($"Download failed {filePath} with code position 2");
  620. return false;
  621. }
  622. tryCount++;
  623. }
  624. else
  625. {
  626. if (cancelTokenSource != null && cancelTokenSource.IsCancellationRequested)
  627. {
  628. Logger.WriteLineError($"Download failed {filePath} with code position 3");
  629. return false;
  630. }
  631. break;
  632. }
  633. }
  634. while (tryCount < 2);
  635. }
  636. if (fileStream.Length == fileSize)
  637. {
  638. isDownloadSuccess = true;
  639. progressCallback?.Invoke(1.0);
  640. if (!string.IsNullOrWhiteSpace(filePath))
  641. {
  642. fileStream.Close();
  643. File.Move(filePath + ".tmp", filePath);
  644. // Logger.WriteLineInfo($"download success, file token:{requestUrl}");
  645. }
  646. }
  647. else {
  648. Logger.WriteLineError($"Download failed since file stream length not equal with filesize" +
  649. $"the stream length is {fileStream?.Length}, the file size is {fileSize}");
  650. }
  651. }
  652. catch (Exception ex)
  653. {
  654. if (!HttpClientHelper.IsFinalAgreement)
  655. {
  656. Logger.WriteLineError($"Downloding ufile error {ex}");
  657. }
  658. else
  659. {
  660. Logger.WriteLineError($"Throw the downloading exception {ex}");
  661. throw ex;
  662. }
  663. }
  664. finally
  665. {
  666. fileStream.Close();
  667. }
  668. return isDownloadSuccess;
  669. }
  670. /// <summary>
  671. /// 下载http请求
  672. /// </summary>
  673. /// <param name="requestUrl"></param>
  674. /// <param name="fileStream"></param>
  675. /// <param name="rangeFrom"></param>
  676. /// <param name="rangeTo"></param>
  677. /// <param name="fileSize"></param>
  678. /// <param name="isKeepAlive"></param>
  679. /// <param name="progressCallback"></param>
  680. /// <returns></returns>
  681. private bool DoHttpRequest(string requestUrl, Stream fileStream, long rangeFrom, long rangeTo, long fileIndex, long fileSize, bool isKeepAlive, Action<double> progressCallback, CancellationTokenSource cancelTokenSource = null)
  682. {
  683. if (fileStream == null)
  684. {
  685. Logger.WriteLineError("fileStream can't be null");
  686. return false;
  687. }
  688. if (string.IsNullOrWhiteSpace(requestUrl))
  689. {
  690. Logger.WriteLineError("requestUrl can't be empty");
  691. return false;
  692. }
  693. try
  694. {
  695. using (var downloadRequest = new HttpRequestMessage())
  696. {
  697. downloadRequest.RequestUri = new Uri(requestUrl);
  698. downloadRequest.Method = HttpMethod.Get;
  699. if (rangeTo > fileSize)
  700. {
  701. rangeTo = fileSize;
  702. }
  703. downloadRequest.Headers.Range = new RangeHeaderValue(rangeFrom, rangeTo);
  704. using (var downloadStream = HttpClientHelper.ExecuteRequest(downloadRequest, HttpClientHelper.GetStreamResult, isKeepAlive,cancelTokenSource))
  705. {
  706. if (downloadStream == null)
  707. {
  708. return false;
  709. }
  710. var readLength = rangeTo - rangeFrom;
  711. var dataToWrite = new byte[readLength];
  712. var downloadLength = downloadStream.Read(dataToWrite, 0, (int)readLength);
  713. fileStream.Write(dataToWrite, 0, downloadLength);
  714. fileIndex = fileStream.Length;
  715. var percentage =(double)fileIndex / fileSize;
  716. progressCallback?.Invoke(percentage);
  717. return true;
  718. }
  719. }
  720. }
  721. catch (Exception ex)
  722. {
  723. Logger.WriteLineWarn($"Download fail, {requestUrl}, {ex}");
  724. return false;
  725. }
  726. }
  727. /// <summary>
  728. /// 获取当前分块的长度
  729. /// </summary>
  730. /// <param name="streamLength"></param>
  731. /// <param name="chunkSize"></param>
  732. /// <param name="fileSize"></param>
  733. /// <returns></returns>
  734. private long GetNewLength(long streamLength, long chunkSize, long fileSize)
  735. {
  736. var newChunk = streamLength + chunkSize;
  737. return newChunk > fileSize ? fileSize : newChunk;
  738. }
  739. /// <summary>
  740. /// 删除文件
  741. /// </summary>
  742. /// <param name="fileName"></param>
  743. /// <returns></returns>
  744. public bool DeleteFile(string requestUrl, string authorization)
  745. {
  746. if (string.IsNullOrWhiteSpace(authorization))
  747. {
  748. Logger.WriteLineError("authorization can't be empty");
  749. return false;
  750. }
  751. if (string.IsNullOrWhiteSpace(requestUrl))
  752. {
  753. Logger.WriteLineError("requestUrl can't be empty");
  754. return false;
  755. }
  756. if (_objectUserAgent == null)
  757. {
  758. throw new Exception("UFileConfig not set");
  759. }
  760. using (var request = new HttpRequestMessage())
  761. {
  762. request.RequestUri = new Uri(requestUrl);
  763. request.Method = HttpMethod.Delete;
  764. request.Headers.UserAgent.Add(new ProductInfoHeaderValue(_objectUserAgent.ProductName, _objectUserAgent.Version));
  765. request.Headers.TryAddWithoutValidation("Authorization", authorization);
  766. return HttpClientHelper.ExecuteRequest(request, HttpClientHelper.GetBooleanResult);
  767. }
  768. }
  769. /// <summary>
  770. /// 查找文件
  771. /// </summary>
  772. /// <param name="prefix"></param>
  773. /// <returns></returns>
  774. public ObjectFileItemList SearchFiles(string requestUrl, string authorization)
  775. {
  776. if (string.IsNullOrWhiteSpace(authorization))
  777. {
  778. Logger.WriteLineError("authorization can't be empty");
  779. return null;
  780. }
  781. if (string.IsNullOrWhiteSpace(requestUrl))
  782. {
  783. Logger.WriteLineError("requestUrl can't be empty");
  784. return null;
  785. }
  786. using (var request = new HttpRequestMessage())
  787. {
  788. request.RequestUri = new Uri(requestUrl);
  789. request.Method = HttpMethod.Get;
  790. request.Headers.UserAgent.Add(new ProductInfoHeaderValue(_objectUserAgent.ProductName, _objectUserAgent.Version));
  791. request.Headers.Add("Authorization", authorization);
  792. return HttpClientHelper.ExecuteRequest(request, HttpClientHelper.GetJsonDataResult<ObjectFileItemList>);
  793. }
  794. }
  795. /// <summary>
  796. /// 批量删除文件
  797. /// </summary>
  798. /// <param name="uFileInfos"></param>
  799. /// <param name="progressCallback"></param>
  800. /// <returns></returns>
  801. public bool DeleteFiles(List<ObjectFileInfo> uFileInfos, Action<double> progressCallback = null)
  802. {
  803. if (_objectUserAgent == null)
  804. {
  805. throw new Exception("UFileConfig not set");
  806. }
  807. ConcurrentQueue<QueueRequestEntity> httpRequestMessageQueue = new ConcurrentQueue<QueueRequestEntity>();
  808. int queueIndex = 1;
  809. foreach (var file in uFileInfos)
  810. {
  811. var request = new HttpRequestMessage();
  812. request.RequestUri = new Uri(file.RequestUrl);
  813. request.Method = HttpMethod.Delete;
  814. request.Headers.Add("Authorization", file.Authorization);
  815. httpRequestMessageQueue.Enqueue(new QueueRequestEntity
  816. {
  817. FileInfo = file,
  818. QueueCount = uFileInfos.Count,
  819. QueueIndex = queueIndex,
  820. ProgressCallback = progressCallback,
  821. Request = request,
  822. CompleteWorker = DeleteFileCallback
  823. });
  824. queueIndex++;
  825. }
  826. var result = HttpClientHelper.ExecuteRequestQueue(httpRequestMessageQueue);
  827. return result;
  828. }
  829. /// <summary>
  830. /// 删除文件回调
  831. /// </summary>
  832. /// <param name="queueWorkItem"></param>
  833. private void DeleteFileCallback(QueueWorkItem queueWorkItem)
  834. {
  835. queueWorkItem.ProgressCallback?.Invoke((int)(100.0 * queueWorkItem.QueueIndex / queueWorkItem.QueueCount));
  836. }
  837. public long GetFileLength(string requestUrl)
  838. {
  839. using (var requestHead = new HttpRequestMessage())
  840. {
  841. try
  842. {
  843. requestHead.RequestUri = new Uri(requestUrl);
  844. requestHead.Method = HttpMethod.Head;
  845. return HttpClientHelper.ExecuteRequest(requestHead, HttpClientHelper.GetLengthResult);
  846. }
  847. catch (Exception ex)
  848. {
  849. Logger.WriteLineError($"Get file head error {ex}");
  850. }
  851. }
  852. return 0;
  853. }
  854. }
  855. }