VCloudStorageUploader.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using Vinno.IUS.Common.IO;
  8. using Vinno.IUS.Common.Log;
  9. using Vinno.IUS.Common.Network.Leaf;
  10. using Vinno.IUS.Common.Network.Tcp;
  11. using Vinno.IUS.Common.Utilities;
  12. using Vinno.vCloud.Common.Storage.Upload;
  13. using Vinno.vCloud.Protocol.Infrastructures;
  14. using Vinno.vCloud.Protocol.Messages.Client.Storage;
  15. namespace Vinno.vCloud.Common.Storage
  16. {
  17. public class VCloudStorageUploader : IUploader
  18. {
  19. /// <summary>
  20. ///when app running ,set cache folder path
  21. /// </summary>
  22. private readonly List<IUploadStorageFileInfo> UploadStorageFileInfoList =
  23. new List<IUploadStorageFileInfo>();
  24. private const int MinBlockSize = 1048576; //4Mb
  25. private const int BlockSize = 4194304; //4mb
  26. /// <summary>
  27. /// Upload one file and return the file token.
  28. /// </summary>
  29. /// <param name="creator">The TcpCrerator for newtwork connection</param>
  30. /// <param name="fileData">The file data to be uploaded</param>
  31. /// <param name="progress">The upload progress</param>
  32. /// <param name="cancelTokenSource">The token to cancel the upload</param>
  33. /// <returns>The file token in server</returns>
  34. public string UploadFile(StorageInfo storageInfo, byte[] fileData, Action<double> progress = null, CancellationTokenSource cancelTokenSource = null, bool isReturnCDNUrl = true, bool isCopyFileToOtherNode = false, Func<string, string> GetAuthentication = null, Func<string, List<KeyValuePair<string, string>>, string> GetCopyAuthentication = null)
  35. {
  36. var storageUrl = storageInfo.Url;
  37. if (string.IsNullOrEmpty(storageUrl)) { throw new Exception("Storage url is empty"); }
  38. var creator = new TcpCreator(storageUrl);
  39. var storageLeaf = new ClientLeaf(new LeafIdContext(), LeafMode.Single, creator);
  40. try
  41. {
  42. using (var stream = new MemoryStream(fileData))
  43. {
  44. if (stream.Length <= BlockSize)
  45. {
  46. var size = (int)stream.Length;
  47. var data = new byte[size];
  48. stream.Read(data, 0, size);
  49. var buffer = new ByteBuffer(data);
  50. var token = storageLeaf.ApplyToken();
  51. var result = storageLeaf.Send(token, new StoreFileRequest { FileData = buffer });
  52. var storeFileSuccess = StoreFileSuccess.Convert(result);
  53. if (storeFileSuccess != null)
  54. {
  55. progress?.Invoke(1.0);
  56. return storeFileSuccess.FileToken;
  57. }
  58. }
  59. else
  60. {
  61. var size = (int)stream.Length;
  62. var count = size / BlockSize;
  63. var totalCount = size % BlockSize != 0 ? count + 1 : count;
  64. for (int i = 0; i < count; i++)
  65. {
  66. if (cancelTokenSource != null && cancelTokenSource.IsCancellationRequested)
  67. {
  68. return null;
  69. }
  70. var data = new byte[BlockSize];
  71. stream.Read(data, 0, BlockSize);
  72. var buffer = new ByteBuffer(data);
  73. var token = storageLeaf.ApplyToken();
  74. var result = storageLeaf.Send(token, new StoreFileRequest { FileData = buffer, Index = i, Count = totalCount });
  75. var storeFileSuccess = StoreFileSuccess.Convert(result);
  76. if (storeFileSuccess == null)
  77. {
  78. throw new InvalidOperationException("Store file error");
  79. }
  80. progress?.Invoke(((double)i + 1) / totalCount);
  81. var isDone = totalCount == count && i == count - 1;
  82. size -= BlockSize;
  83. if (isDone)
  84. {
  85. return storeFileSuccess.FileToken;
  86. }
  87. }
  88. if (size > 0)
  89. {
  90. if (cancelTokenSource != null && cancelTokenSource.IsCancellationRequested)
  91. {
  92. return null;
  93. }
  94. var data = new byte[size];
  95. stream.Read(data, 0, size);
  96. var buffer = new ByteBuffer(data);
  97. var token = storageLeaf.ApplyToken();
  98. var result = storageLeaf.Send(token, new StoreFileRequest
  99. { FileData = buffer, Index = totalCount - 1, Count = totalCount });
  100. var storeFileSuccess = StoreFileSuccess.Convert(result);
  101. if (storeFileSuccess == null)
  102. {
  103. throw new InvalidOperationException("Store file error");
  104. }
  105. progress?.Invoke(1.0);
  106. return storeFileSuccess.FileToken;
  107. }
  108. }
  109. }
  110. }
  111. finally
  112. {
  113. storageLeaf.Close();
  114. }
  115. throw new InvalidOperationException("Store file failed.");
  116. }
  117. /// <summary>
  118. /// new upload file(Breakpoint continuingly)
  119. /// </summary>
  120. /// <param name="creator"></param>
  121. /// <param name="filePath"></param>
  122. /// <param name="accountId">current account id</param>
  123. /// <param name="progress"> UI action progress</param>
  124. /// <param name="cancelTokenSource"></param>
  125. /// <returns>file token</returns>
  126. public string UploadFile(StorageInfo storageInfo, string filePath, string accountId,
  127. Action<double> progress = null,
  128. CancellationTokenSource cancelTokenSource = null,bool isReturnCDNUrl = true, bool isCopyFileToOtherNode = false, Func<string, string> GetAuthentication = null, Func<string, List<KeyValuePair<string, string>>, string> GetCopyAuthentication = null)
  129. {
  130. var storageUrl = storageInfo.Url;
  131. var creator = new TcpCreator(storageUrl);
  132. var storageLeaf = new ClientLeaf(new LeafIdContext(), LeafMode.Single, creator);
  133. try
  134. {
  135. if (!storageLeaf.Online)
  136. {
  137. throw new Exception($"storageLeaf is offline for upload file,url:{storageInfo}");
  138. }
  139. using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
  140. {
  141. Logger.WriteLineInfo($"UploadStoreFile:filePath{filePath} begin");
  142. var uploadFileToken = UploadFileFromStream(stream,
  143. accountId, progress, storageLeaf, cancelTokenSource);
  144. return uploadFileToken;
  145. }
  146. }
  147. finally
  148. {
  149. storageLeaf.Close();
  150. }
  151. }
  152. /// <summary>
  153. /// new upload file(Breakpoint continuingly)
  154. /// </summary>
  155. /// <param name="filePath">The upload file path</param>
  156. /// <param name="fileName">The file name in file server.</param>
  157. /// <param name="accountId">The account id</param>
  158. /// <param name="storageInfo">The storage info.</param>
  159. /// <param name="progress"> UI action progress</param>
  160. /// <param name="cancelTokenSource"></param>
  161. /// <returns>file token</returns>
  162. public string UploadFile(StorageInfo storageInfo, string filePath, string fileName, string accountId, Action<double> progress, CancellationTokenSource cancelTokenSource, bool isReturnCDNUrl, bool isCopyFileToOtherNode, Func<string, string> GetAuthentication, Func<string, List<KeyValuePair<string, string>>, string> GetCopyAuthentication = null)
  163. {
  164. var storageUrl = storageInfo.Url;
  165. var creator = new TcpCreator(storageUrl);
  166. var storageLeaf = new ClientLeaf(new LeafIdContext(), LeafMode.Single, creator);
  167. try
  168. {
  169. if (!storageLeaf.Online)
  170. {
  171. throw new Exception($"storageLeaf is offline for upload file,url:{storageInfo}");
  172. }
  173. using (var stream = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
  174. {
  175. Logger.WriteLineInfo($"UploadStoreFile:filePath{filePath} begin");
  176. var uploadFileToken = UploadFileFromStream(fileName,stream,
  177. progress, storageLeaf, cancelTokenSource);
  178. return uploadFileToken;
  179. }
  180. }
  181. finally
  182. {
  183. storageLeaf.Close();
  184. }
  185. }
  186. /// <summary>
  187. /// Upload file to server by stream
  188. /// </summary>
  189. /// <param name="stream"></param>
  190. /// <param name="accountId"></param>
  191. /// <param name="progress"></param>
  192. /// <param name="storageLeaf"></param>
  193. /// <param name="cancelTokenSource"></param>
  194. /// <returns>file token</returns>
  195. private string UploadFileFromStream(string fileName,Stream stream,Action<double> progress, ClientLeaf storageLeaf,
  196. CancellationTokenSource cancelTokenSource = null)
  197. {
  198. progress?.Invoke(0.01);
  199. var fileSize = (int)stream.Length;
  200. var md5 = MD5.GetHashString(stream);
  201. var currentUploadFileInfo = new UploadStorageFileInfo()
  202. {
  203. CacheFileName = fileName,
  204. FilePosition = 0,
  205. FileSize = fileSize,
  206. Id = string.Empty,
  207. MD5 = md5
  208. };
  209. while (fileSize > currentUploadFileInfo.FilePosition + 1)
  210. {
  211. if (cancelTokenSource != null && cancelTokenSource.IsCancellationRequested)
  212. {
  213. //upload file canceled
  214. return null;
  215. }
  216. var token = storageLeaf.ApplyToken();
  217. var createuploadRequest = CreateUploadRequest(stream, "", currentUploadFileInfo, fileSize);
  218. var result = storageLeaf.Send(token, createuploadRequest);
  219. var storeFileResult = UploadStoreFileResult.Convert(result);
  220. if (storeFileResult != null)
  221. {
  222. var storeFileInfoMessage = storeFileResult.ClientUploadStorageFileInfoMessage;
  223. currentUploadFileInfo.Id = storeFileInfoMessage.Id;
  224. currentUploadFileInfo.CacheFileName = storeFileInfoMessage.CacheFileName;
  225. currentUploadFileInfo.FilePosition = storeFileInfoMessage.FilePosition;
  226. if (!string.IsNullOrWhiteSpace(storeFileInfoMessage.FileToken))
  227. {
  228. // upload file success
  229. currentUploadFileInfo.FileToken = storeFileInfoMessage.FileToken;
  230. progress?.Invoke(1.0);
  231. return currentUploadFileInfo.FileToken;
  232. }
  233. progress?.Invoke((double)currentUploadFileInfo.FilePosition / fileSize);
  234. }
  235. else
  236. {
  237. throw new InvalidOperationException("Store file error");
  238. }
  239. }
  240. return currentUploadFileInfo.FileToken;
  241. }
  242. /// <summary>
  243. /// Upload file to server by stream
  244. /// </summary>
  245. /// <param name="stream"></param>
  246. /// <param name="accountId"></param>
  247. /// <param name="progress"></param>
  248. /// <param name="storageLeaf"></param>
  249. /// <param name="cancelTokenSource"></param>
  250. /// <returns>file token</returns>
  251. private string UploadFileFromStream(Stream stream,
  252. string accountId, Action<double> progress, ClientLeaf storageLeaf,
  253. CancellationTokenSource cancelTokenSource = null)
  254. {
  255. progress?.Invoke(0.01);
  256. var fileSize = (int)stream.Length;
  257. var md5 = MD5.GetHashString(stream);
  258. var currentUploadFileInfo = new UploadStorageFileInfo()
  259. {
  260. CacheFileName = string.Empty,
  261. FilePosition = 0,
  262. FileSize = fileSize,
  263. Id = string.Empty,
  264. MD5 = md5
  265. };
  266. currentUploadFileInfo.FileSize = fileSize;
  267. while (fileSize > currentUploadFileInfo.FilePosition + 1)
  268. {
  269. if (cancelTokenSource != null && cancelTokenSource.IsCancellationRequested)
  270. {
  271. //upload file canceled
  272. return null;
  273. }
  274. var token = storageLeaf.ApplyToken();
  275. var createuploadRequest = CreateUploadRequest(stream, accountId, currentUploadFileInfo, fileSize);
  276. var result = storageLeaf.Send(token, createuploadRequest);
  277. var storeFileResult = UploadStoreFileResult.Convert(result);
  278. if (storeFileResult != null)
  279. {
  280. var storeFileInfoMessage = storeFileResult.ClientUploadStorageFileInfoMessage;
  281. currentUploadFileInfo.Id = storeFileInfoMessage.Id;
  282. currentUploadFileInfo.CacheFileName = storeFileInfoMessage.CacheFileName;
  283. currentUploadFileInfo.FilePosition = storeFileInfoMessage.FilePosition;
  284. if (!string.IsNullOrWhiteSpace(storeFileInfoMessage.FileToken))
  285. {
  286. // upload file success
  287. currentUploadFileInfo.FileToken = storeFileInfoMessage.FileToken;
  288. progress?.Invoke(1.0);
  289. return currentUploadFileInfo.FileToken;
  290. }
  291. progress?.Invoke((double)currentUploadFileInfo.FilePosition / fileSize);
  292. }
  293. else
  294. {
  295. throw new InvalidOperationException("Store file error");
  296. }
  297. }
  298. return currentUploadFileInfo.FileToken;
  299. }
  300. private async Task<string> UploadFileFromStreamAsync(Stream stream,
  301. string accountId, Action<double> progress, ClientLeaf storageLeaf,
  302. CancellationTokenSource cancelTokenSource = null)
  303. {
  304. progress?.Invoke(0.01);
  305. var fileSize = (int)stream.Length;
  306. var md5 = MD5.GetHashString(stream);
  307. var currentUploadFileInfo = UploadStorageFileInfoList.FirstOrDefault(v => v.MD5 == md5);
  308. if (currentUploadFileInfo == null)
  309. {
  310. currentUploadFileInfo = new UploadStorageFileInfo()
  311. {
  312. CacheFileName = string.Empty,
  313. FilePosition = 0,
  314. FileSize = fileSize,
  315. Id = string.Empty,
  316. MD5 = md5
  317. };
  318. UploadStorageFileInfoList.Add(currentUploadFileInfo);
  319. }
  320. currentUploadFileInfo.FileSize = fileSize;
  321. while (fileSize > currentUploadFileInfo.FilePosition + 1)
  322. {
  323. if (cancelTokenSource != null && cancelTokenSource.IsCancellationRequested)
  324. {
  325. //upload file canceled
  326. return null;
  327. }
  328. var token = storageLeaf.ApplyToken();
  329. var result = await storageLeaf.SendAsync(token, CreateUploadRequest(
  330. stream, accountId, currentUploadFileInfo, fileSize));
  331. var storeFileResult = UploadStoreFileResult.Convert(result);
  332. if (storeFileResult != null)
  333. {
  334. var storeFileInfoMessage = storeFileResult.ClientUploadStorageFileInfoMessage;
  335. currentUploadFileInfo.Id = storeFileInfoMessage.Id;
  336. currentUploadFileInfo.FileToken = storeFileInfoMessage.FileToken;
  337. currentUploadFileInfo.CacheFileName = storeFileInfoMessage.CacheFileName;
  338. currentUploadFileInfo.FilePosition = storeFileInfoMessage.FilePosition;
  339. if (!string.IsNullOrEmpty(currentUploadFileInfo.FileToken))
  340. {
  341. // upload file success
  342. progress?.Invoke(1.0);
  343. return currentUploadFileInfo.FileToken;
  344. }
  345. progress?.Invoke((double)currentUploadFileInfo.FilePosition / fileSize);
  346. }
  347. else
  348. {
  349. throw new InvalidOperationException("Store file error");
  350. }
  351. Thread.Sleep(10);
  352. }
  353. return currentUploadFileInfo.FileToken;
  354. }
  355. private UploadStoreFileRequest CreateUploadRequest(Stream stream, string accountId,
  356. IUploadStorageFileInfo currentUploadFileInfo, int fileSize)
  357. {
  358. Logger.WriteLineInfo($"UploadStoreFile CreateUploadRequest begin");
  359. //set this upload file end position byte
  360. var lastPosition = currentUploadFileInfo.FilePosition + BlockSize - 1;
  361. var fileSizePostion = fileSize - 1;
  362. if (lastPosition > fileSizePostion)
  363. {
  364. int offet = fileSizePostion - currentUploadFileInfo.FilePosition;
  365. if (offet > MinBlockSize)
  366. {
  367. lastPosition = currentUploadFileInfo.FilePosition + MinBlockSize;
  368. }
  369. else
  370. {
  371. lastPosition = fileSizePostion;
  372. }
  373. }
  374. if (lastPosition == fileSizePostion)
  375. {
  376. lastPosition = fileSizePostion;
  377. }
  378. //set this upload file byte size
  379. var blockSize = lastPosition - currentUploadFileInfo.FilePosition + 1;
  380. stream.Position = currentUploadFileInfo.FilePosition;
  381. var data = new byte[blockSize];
  382. stream.Read(data, 0, blockSize);
  383. var buffer = new ByteBuffer(data);
  384. Logger.WriteLineInfo($"UploadStoreFile blockSize: {blockSize} filePosition:{currentUploadFileInfo.FilePosition}");
  385. // upload to server
  386. var uploadmessage = new ClientUploadStorageFileInfoMessage
  387. {
  388. FileData = buffer,
  389. Id = currentUploadFileInfo.Id,
  390. FileMd5 = currentUploadFileInfo.MD5,
  391. AccountId = accountId ?? string.Empty,
  392. FileSize = currentUploadFileInfo.FileSize,
  393. FilePosition = currentUploadFileInfo.FilePosition,
  394. FileToken = currentUploadFileInfo.FileToken,
  395. CacheFileName = currentUploadFileInfo.CacheFileName
  396. };
  397. return new UploadStoreFileRequest { ClientUploadStorageFileInfoMessage = uploadmessage };
  398. }
  399. /// <summary>
  400. /// upload file async
  401. /// </summary>
  402. /// <param name="storageUrl"></param>
  403. /// <param name="fileData"></param>
  404. /// <param name="progress"></param>
  405. /// <param name="cancelTokenSource"></param>
  406. /// <returns></returns>
  407. public async Task<string> UploadFileAsync(StorageInfo storageInfo, byte[] fileData, Action<double> progress, CancellationTokenSource cancelTokenSource, bool isReturnCDNUrl = true, bool isCopyFileToOtherNode = false, Func<string, string> GetAuthentication = null, Func<string, List<KeyValuePair<string, string>>, string> GetCopyAuthentication = null)
  408. {
  409. var storageUrl = storageInfo.Url;
  410. if (string.IsNullOrEmpty(storageUrl)) { throw new Exception($"Storage url is empty, {storageInfo}"); }
  411. var creator = new TcpCreator(storageUrl);
  412. var storageLeaf = new ClientLeaf(new LeafIdContext(), LeafMode.Single, creator);
  413. try
  414. {
  415. using (var stream = new MemoryStream(fileData))
  416. {
  417. if (stream.Length <= BlockSize)
  418. {
  419. var size = (int)stream.Length;
  420. var data = new byte[size];
  421. await stream.ReadAsync(data, 0, size);
  422. var buffer = new ByteBuffer(data);
  423. var token = storageLeaf.ApplyToken();
  424. var result = await storageLeaf.SendAsync(token, new StoreFileRequest { FileData = buffer });
  425. var storeFileSuccess = StoreFileSuccess.Convert(result);
  426. if (storeFileSuccess != null)
  427. {
  428. progress?.Invoke(1.0);
  429. return storeFileSuccess.FileToken;
  430. }
  431. }
  432. else
  433. {
  434. var size = (int)stream.Length;
  435. var count = size / BlockSize;
  436. var totalCount = size % BlockSize != 0 ? count + 1 : count;
  437. for (int i = 0; i < count; i++)
  438. {
  439. if (cancelTokenSource != null && cancelTokenSource.IsCancellationRequested)
  440. {
  441. return null;
  442. }
  443. var data = new byte[BlockSize];
  444. await stream.ReadAsync(data, 0, BlockSize);
  445. var buffer = new ByteBuffer(data);
  446. var token = storageLeaf.ApplyToken();
  447. var result = await storageLeaf.SendAsync(token, new StoreFileRequest { FileData = buffer, Index = i, Count = totalCount });
  448. var storeFileSuccess = StoreFileSuccess.Convert(result);
  449. if (storeFileSuccess == null)
  450. {
  451. throw new InvalidOperationException("Store file error");
  452. }
  453. progress?.Invoke(((double)i + 1) / totalCount);
  454. var isDone = totalCount == count && i == count - 1;
  455. size -= BlockSize;
  456. if (isDone)
  457. {
  458. return storeFileSuccess.FileToken;
  459. }
  460. }
  461. if (size > 0)
  462. {
  463. if (cancelTokenSource != null && cancelTokenSource.IsCancellationRequested)
  464. {
  465. return null;
  466. }
  467. var data = new byte[size];
  468. stream.Read(data, 0, size);
  469. var buffer = new ByteBuffer(data);
  470. var token = storageLeaf.ApplyToken();
  471. var result = await storageLeaf.SendAsync(token, new StoreFileRequest
  472. { FileData = buffer, Index = totalCount - 1, Count = totalCount });
  473. var storeFileSuccess = StoreFileSuccess.Convert(result);
  474. if (storeFileSuccess == null)
  475. {
  476. throw new InvalidOperationException("Store file error");
  477. }
  478. progress?.Invoke(1.0);
  479. return storeFileSuccess.FileToken;
  480. }
  481. }
  482. }
  483. }
  484. finally
  485. {
  486. storageLeaf.Close();
  487. }
  488. throw new InvalidOperationException("Store file failed.");
  489. }
  490. /// <summary>
  491. /// new upload file(Breakpoint continuingly)
  492. /// </summary>
  493. /// <param name="filePath">The upload file path</param>
  494. /// <param name="fileName">The file name in file server.</param>
  495. /// <param name="storageInfo">The storage info.</param>
  496. /// <returns>file token</returns>
  497. public List<string> CopydFile(StorageInfo storageInfo, string filePath, string fileName, string destinationServer, Func<string, List<KeyValuePair<string, string>>, string> GetCopyAuthentication = null)
  498. {
  499. return new List<string>();
  500. }
  501. }
  502. }