Quick.CloudStorage.Provider.Azure.pas 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. { ***************************************************************************
  2. Copyright (c) 2016-2019 Kike Pérez
  3. Unit : Quick.CloudStorage.Provider.Azure
  4. Description : CloudStorage Azure provider
  5. Author : Kike Pérez
  6. Version : 1.8
  7. Created : 14/10/2018
  8. Modified : 07/10/2019
  9. This file is part of QuickLib: https://github.com/exilon/QuickLib
  10. ***************************************************************************
  11. Licensed under the Apache License, Version 2.0 (the "License");
  12. you may not use this file except in compliance with the License.
  13. You may obtain a copy of the License at
  14. http://www.apache.org/licenses/LICENSE-2.0
  15. Unless required by applicable law or agreed to in writing, software
  16. distributed under the License is distributed on an "AS IS" BASIS,
  17. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  18. See the License for the specific language governing permissions and
  19. limitations under the License.
  20. *************************************************************************** }
  21. unit Quick.CloudStorage.Provider.Azure;
  22. {$i QuickLib.inc}
  23. interface
  24. uses
  25. Classes,
  26. System.SysUtils,
  27. System.Generics.Collections,
  28. IPPeerClient,
  29. Quick.Commons,
  30. Quick.CloudStorage,
  31. Data.Cloud.CloudAPI,
  32. Data.Cloud.AzureAPI;
  33. type
  34. TCloudStorageAzureProvider = class(TCloudStorageProvider)
  35. private
  36. fAzureConnection : TAzureConnectionInfo;
  37. fAzureID : string;
  38. fAzureKey : string;
  39. procedure SetSecure(aValue: Boolean); override;
  40. function ListContainers(azContainersStartWith : string; azResponseInfo : TResponseInfo) : TStrings;
  41. public
  42. constructor Create; overload; override;
  43. constructor Create(const aAccountName, aAccountKey : string); overload;
  44. destructor Destroy; override;
  45. function GetRootFolders : TStrings; override;
  46. procedure OpenDir(const aPath : string); override;
  47. function GetFile(const aPath: string; out stream : TStream) : Boolean; override;
  48. function GetURL(const aPath : string) : string; override;
  49. end;
  50. implementation
  51. { TCloudExplorerProvider }
  52. constructor TCloudStorageAzureProvider.Create;
  53. begin
  54. fAzureConnection := TAzureConnectionInfo.Create(nil);
  55. end;
  56. constructor TCloudStorageAzureProvider.Create(const aAccountName, aAccountKey : string);
  57. begin
  58. inherited Create;
  59. Create;
  60. fAzureID := aAccountName;
  61. fAzureKey := aAccountKey;
  62. fAzureConnection.AccountName := aAccountName;
  63. fAzureConnection.AccountKey := aAccountKey;
  64. end;
  65. destructor TCloudStorageAzureProvider.Destroy;
  66. begin
  67. if Assigned(fAzureConnection) then fAzureConnection.Free;
  68. inherited;
  69. end;
  70. function TCloudStorageAzureProvider.GetFile(const aPath: string; out stream : TStream) : Boolean;
  71. var
  72. BlobService : TAzureBlobService;
  73. CloudResponseInfo : TCloudResponseInfo;
  74. begin
  75. BlobService := TAzureBlobService.Create(fAzureConnection);
  76. try
  77. CloudResponseInfo := TCloudResponseInfo.Create;
  78. try
  79. Result := BlobService.GetBlob(RootFolder,aPath,stream,'',CloudResponseInfo);
  80. if not Result then raise Exception.CreateFmt('Cloud error %d : %s',[CloudResponseInfo.StatusCode,CloudResponseInfo.StatusMessage]);
  81. finally
  82. CloudResponseInfo.Free;
  83. end;
  84. finally
  85. BlobService.Free;
  86. end;
  87. end;
  88. function TCloudStorageAzureProvider.GetRootFolders: TStrings;
  89. var
  90. respinfo : TResponseInfo;
  91. begin
  92. Result := ListContainers('',respinfo);
  93. end;
  94. function TCloudStorageAzureProvider.GetURL(const aPath: string): string;
  95. begin
  96. Result := Format('https://%s.blob.core.windows.net/%s/%s',[fAzureConnection.AccountName,RootFolder,aPath]);
  97. end;
  98. function TCloudStorageAzureProvider.ListContainers(azContainersStartWith : string; azResponseInfo : TResponseInfo) : TStrings;
  99. var
  100. BlobService : TAzureBlobService;
  101. CloudResponseInfo : TCloudResponseInfo;
  102. cNextMarker : string;
  103. AzParams : TStrings;
  104. AzContainer : TAzureContainer;
  105. AzContainers : TList<TAzureContainer>;
  106. begin
  107. Result := TStringList.Create;
  108. cNextMarker := '';
  109. BlobService := TAzureBlobService.Create(fAzureConnection);
  110. CloudResponseInfo := TCloudResponseInfo.Create;
  111. try
  112. BlobService.Timeout := Timeout;
  113. repeat
  114. AzParams := TStringList.Create;
  115. try
  116. if azContainersStartWith <> '' then AzParams.Values['prefix'] := azContainersStartWith;
  117. if cNextMarker <> '' then AzParams.Values['marker'] := cNextMarker;
  118. AzContainers := BlobService.ListContainers(cNextMarker,AzParams,CloudResponseInfo);
  119. try
  120. azResponseInfo.Get(CloudResponseInfo);
  121. if (azResponseInfo.StatusCode = 200) and (Assigned(AzContainers)) then
  122. begin
  123. for AzContainer in AzContainers do
  124. begin
  125. Result.Add(AzContainer.Name);
  126. end;
  127. end;
  128. finally
  129. if Assigned(AzContainer) then
  130. begin
  131. //frees ContainerList objects
  132. for AzContainer in AzContainers do AzContainer.Free;
  133. AzContainers.Free;
  134. end;
  135. end;
  136. finally
  137. AzParams.Free;
  138. end;
  139. until (cNextMarker = '') or (azResponseInfo.StatusCode <> 200);
  140. finally
  141. BlobService.Free;
  142. CloudResponseInfo.Free;
  143. end;
  144. end;
  145. {$IFDEF DELPHITOKYO_UP}
  146. procedure TCloudStorageAzureProvider.OpenDir(const aPath: string);
  147. var
  148. BlobService : TAzureBlobService;
  149. azBlob : TAzureBlobItem;
  150. DirItem : TCloudItem;
  151. CloudResponseInfo : TCloudResponseInfo;
  152. cNextMarker : string;
  153. azBlobList : TArray<TAzureBlobItem>;
  154. blobprefix : TArray<string>;
  155. xmlresp : string;
  156. azResponseInfo : TResponseInfo;
  157. azContainer : string;
  158. folder : string;
  159. prop : TPair<string,string>;
  160. begin
  161. Status := stSearching;
  162. cNextMarker := '';
  163. if aPath = '..' then
  164. begin
  165. CurrentPath := RemoveLastPathSegment(CurrentPath);
  166. end
  167. else
  168. begin
  169. if (CurrentPath = '') or (aPath.StartsWith('/')) then CurrentPath := aPath
  170. else CurrentPath := CurrentPath + aPath;
  171. end;
  172. if Assigned(OnBeginReadDir) then OnBeginReadDir(CurrentPath);
  173. if CurrentPath.StartsWith('/') then CurrentPath := Copy(CurrentPath,2,CurrentPath.Length);
  174. if (not CurrentPath.IsEmpty) and (not CurrentPath.EndsWith('/')) then CurrentPath := CurrentPath + '/';
  175. azContainer := RootFolder;
  176. if azContainer = '' then azContainer := '$root';
  177. BlobService := TAzureBlobService.Create(fAzureConnection);
  178. try
  179. BlobService.Timeout := Timeout;
  180. Status := stRetrieving;
  181. if Assigned(OnGetListItem) then
  182. begin
  183. DirItem := TCloudItem.Create;
  184. try
  185. DirItem.Name := '..';
  186. DirItem.IsDir := True;
  187. DirItem.Date := 0;
  188. OnGetListItem(DirItem);
  189. finally
  190. DirItem.Free;
  191. end;
  192. end;
  193. repeat
  194. if not (Status in [stSearching,stRetrieving]) then Exit;
  195. if fCancelOperation then
  196. begin
  197. fCancelOperation := False;
  198. Exit;
  199. end;
  200. CloudResponseInfo := TCloudResponseInfo.Create;
  201. try
  202. azBlobList := BlobService.ListBlobs(azContainer,CurrentPath,'/',cNextMarker,100,[],cNextMarker,blobprefix,xmlresp,CloudResponseInfo);
  203. azResponseInfo.Get(CloudResponseInfo);
  204. if azResponseInfo.StatusCode = 200 then
  205. begin
  206. //get folders (prefix)
  207. for folder in blobprefix do
  208. begin
  209. if not (Status in [stSearching,stRetrieving]) then Exit;
  210. DirItem := TCloudItem.Create;
  211. try
  212. if folder.EndsWith('/') then DirItem.Name := RemoveLastChar(folder)
  213. else DirItem.Name := folder;
  214. DirItem.Name := Copy(DirItem.Name,DirItem.Name.LastDelimiter('/')+2,DirItem.Name.Length);
  215. DirItem.IsDir := True;
  216. if Assigned(OnGetListItem) then OnGetListItem(DirItem);
  217. finally
  218. DirItem.Free;
  219. end;
  220. end;
  221. //get files (blobs)
  222. for azBlob in azBlobList do
  223. begin
  224. if not (Status in [stSearching,stRetrieving]) then Exit;
  225. if fCancelOperation then
  226. begin
  227. fCancelOperation := False;
  228. Exit;
  229. end;
  230. DirItem := TCloudItem.Create;
  231. try
  232. DirItem.Name := azBlob.Name;
  233. if DirItem.Name.StartsWith(CurrentPath) then DirItem.Name := StringReplace(DirItem.Name,CurrentPath,'',[]);
  234. if DirItem.Name.Contains('/') then
  235. begin
  236. DirItem.IsDir := True;
  237. DirItem.Name := Copy(DirItem.Name,1,DirItem.Name.IndexOf('/'));
  238. end
  239. else
  240. begin
  241. DirItem.IsDir := False;
  242. for prop in azBlob.Properties do
  243. begin
  244. if prop.Key = 'Content-Length' then DirItem.Size := StrToInt64Def(prop.Value,0)
  245. else if prop.Key = 'Last-Modified' then DirItem.Date := GMT2DateTime(prop.Value);
  246. end;
  247. end;
  248. if Assigned(OnGetListItem) then OnGetListItem(DirItem);
  249. finally
  250. DirItem.Free;
  251. end;
  252. end;
  253. end
  254. else
  255. begin
  256. Status := stFailed;
  257. Exit;
  258. end;
  259. finally
  260. CloudResponseInfo.Free;
  261. end;
  262. if Assigned(OnRefreshReadDir) then OnRefreshReadDir(CurrentPath);
  263. until (cNextMarker = '') or (azResponseInfo.StatusCode <> 200);
  264. Status := stDone;
  265. finally
  266. BlobService.Free;
  267. if Assigned(OnEndReadDir) then OnEndReadDir(CurrentPath);
  268. end;
  269. end;
  270. {$ELSE}
  271. procedure TCloudStorageAzureProvider.OpenDir(const aPath: string);
  272. var
  273. BlobService : TAzureBlobService;
  274. azBlob : TAzureBlob;
  275. DirItem : TCloudItem;
  276. CloudResponseInfo : TCloudResponseInfo;
  277. cNextMarker : string;
  278. azBlobList : TList<TAzureBlob>;
  279. AzParams : TStrings;
  280. azResponseInfo : TResponseInfo;
  281. azContainer : string;
  282. begin
  283. Status := stSearching;
  284. cNextMarker := '';
  285. if aPath = '..' then
  286. begin
  287. CurrentPath := RemoveLastPathSegment(CurrentPath);
  288. end
  289. else
  290. begin
  291. if (CurrentPath = '') or (aPath.StartsWith('/')) then CurrentPath := aPath
  292. else CurrentPath := CurrentPath + aPath;
  293. end;
  294. if Assigned(OnBeginReadDir) then OnBeginReadDir(CurrentPath);
  295. if CurrentPath.StartsWith('/') then CurrentPath := Copy(CurrentPath,2,CurrentPath.Length);
  296. if (not CurrentPath.IsEmpty) and (not CurrentPath.EndsWith('/')) then CurrentPath := CurrentPath + '/';
  297. azContainer := RootFolder;
  298. if azContainer = '' then azContainer := '$root';
  299. BlobService := TAzureBlobService.Create(fAzureConnection);
  300. try
  301. BlobService.Timeout := Timeout;
  302. Status := stRetrieving;
  303. if Assigned(OnGetListItem) then
  304. begin
  305. DirItem := TCloudItem.Create;
  306. try
  307. DirItem.Name := '..';
  308. DirItem.IsDir := True;
  309. DirItem.Date := 0;
  310. OnGetListItem(DirItem);
  311. finally
  312. DirItem.Free;
  313. end;
  314. end;
  315. repeat
  316. if not (Status in [stSearching,stRetrieving]) then Exit;
  317. AzParams := TStringList.Create;
  318. try
  319. if fCancelOperation then
  320. begin
  321. fCancelOperation := False;
  322. Exit;
  323. end;
  324. AzParams.Values['prefix'] := CurrentPath;
  325. //if not Recursive then
  326. AzParams.Values['delimiter'] := '/';
  327. AzParams.Values['maxresults'] := '100';
  328. if cNextMarker <> '' then AzParams.Values['marker'] := cNextMarker;
  329. CloudResponseInfo := TCloudResponseInfo.Create;
  330. try
  331. azBlobList := BlobService.ListBlobs(azContainer,cNextMarker,AzParams,CloudResponseInfo);
  332. azResponseInfo.Get(CloudResponseInfo);
  333. if azResponseInfo.StatusCode = 200 then
  334. begin
  335. try
  336. for azBlob in azBlobList do
  337. begin
  338. if not (Status in [stSearching,stRetrieving]) then Exit;
  339. if fCancelOperation then
  340. begin
  341. fCancelOperation := False;
  342. Exit;
  343. end;
  344. DirItem := TCloudItem.Create;
  345. try
  346. DirItem.Name := azBlob.Name;
  347. if DirItem.Name.StartsWith(CurrentPath) then DirItem.Name := StringReplace(DirItem.Name,CurrentPath,'',[]);
  348. if DirItem.Name.Contains('/') then
  349. begin
  350. DirItem.IsDir := True;
  351. DirItem.Name := Copy(DirItem.Name,1,DirItem.Name.IndexOf('/'));
  352. end
  353. else
  354. begin
  355. DirItem.IsDir := False;
  356. DirItem.Size := StrToInt64Def(azBlob.Properties.Values['Content-Length'],0);
  357. DirItem.Date := GMT2DateTime(azBlob.Properties.Values['Last-Modified']);
  358. end;
  359. if Assigned(OnGetListItem) then OnGetListItem(DirItem);
  360. finally
  361. DirItem.Free;
  362. end;
  363. azBlob.Free;
  364. end;
  365. finally
  366. //frees azbloblist objects
  367. //for azBlob in azBlobList do azBlob.Free;
  368. azBlobList.Free;
  369. end;
  370. end
  371. else
  372. begin
  373. Status := stFailed;
  374. Exit;
  375. end;
  376. finally
  377. CloudResponseInfo.Free;
  378. end;
  379. finally
  380. FreeAndNil(AzParams);
  381. end;
  382. if Assigned(OnRefreshReadDir) then OnRefreshReadDir(CurrentPath);
  383. until (cNextMarker = '') or (azResponseInfo.StatusCode <> 200);
  384. Status := stDone;
  385. finally
  386. BlobService.Free;
  387. if Assigned(OnEndReadDir) then OnEndReadDir(CurrentPath);
  388. end;
  389. end;
  390. {$ENDIF}
  391. {procedure TCloudStorageAzureProvider.OpenDir(const aPath : string);
  392. var
  393. lista : TBlobList;
  394. Blob : TAzureBlobObject;
  395. i : Integer;
  396. azurefilter : string;
  397. DirItem : TCloudItem;
  398. respinfo : TAzureResponseInfo;
  399. begin
  400. if aPath = '..' then
  401. begin
  402. CurrentPath := RemoveLastPathSegment(CurrentPath);
  403. end
  404. else
  405. begin
  406. if CurrentPath = '' then CurrentPath := aPath
  407. else CurrentPath := CurrentPath + aPath;
  408. end;
  409. if Assigned(OnBeginReadDir) then OnBeginReadDir(CurrentPath);
  410. if CurrentPath.StartsWith('/') then CurrentPath := Copy(CurrentPath,2,CurrentPath.Length);
  411. if (not CurrentPath.IsEmpty) and (not CurrentPath.EndsWith('/')) then CurrentPath := CurrentPath + '/';
  412. Status := stRetrieving;
  413. lista := fAzure.ListBlobs(RootFolder,CurrentPath,False,respinfo);
  414. try
  415. if Assigned(lista) then
  416. begin
  417. if Assigned(OnGetListItem) then
  418. begin
  419. DirItem := TCloudItem.Create;
  420. try
  421. DirItem.Name := '..';
  422. DirItem.IsDir := True;
  423. DirItem.Date := 0;
  424. OnGetListItem(DirItem);
  425. finally
  426. DirItem.Free;
  427. end;
  428. end;
  429. end;
  430. if respinfo.StatusCode = 200 then
  431. begin
  432. for Blob in lista do
  433. begin
  434. DirItem := TCloudItem.Create;
  435. try
  436. if Blob.Name.StartsWith(CurrentPath) then Blob.Name := StringReplace(Blob.Name,CurrentPath,'',[]);
  437. if Blob.Name.Contains('/') then
  438. begin
  439. DirItem.IsDir := True;
  440. DirItem.Name := Copy(Blob.Name,1,Blob.Name.IndexOf('/'));
  441. end
  442. else
  443. begin
  444. DirItem.IsDir := False;
  445. DirItem.Name := Blob.Name;
  446. DirItem.Size := Blob.Size;
  447. DirItem.Date := Blob.LastModified;
  448. end;
  449. if Assigned(OnGetListItem) then OnGetListItem(DirItem);
  450. finally
  451. DirItem.Free;
  452. end;
  453. end;
  454. Status := stDone;
  455. end
  456. else Status := stFailed;
  457. finally
  458. lista.Free;
  459. ResponseInfo.Get(respinfo.StatusCode,respinfo.StatusMsg);
  460. end;
  461. end;}
  462. procedure TCloudStorageAzureProvider.SetSecure(aValue: Boolean);
  463. begin
  464. inherited;
  465. if aValue then fAzureConnection.Protocol := 'HTTPS'
  466. else fAzureConnection.Protocol := 'HTTP';
  467. end;
  468. end.