Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

245 linhas
6.2 KiB

  1. unit uvfsZipArchive;
  2. interface
  3. uses
  4. SysUtils, Classes, uvfsManager, KAZip, syncobjs;
  5. type
  6. { TvfsZipArchive }
  7. TvfsZipArchive = class(TvfsProvider)
  8. private
  9. FZipActionCS: TCriticalSection;
  10. FZipper: TKAZip;
  11. procedure Flush(Handle: TvfsStreamHandleWrite; Data: Pointer; DataSize: integer);
  12. function PrepareFilename(const aFilename: String): String;
  13. public
  14. constructor Create(const FileSpec:string; const ExtendedData: string=''); override;
  15. destructor Destroy; override;
  16. class function StorageName: string; override;
  17. function StorageGetFileSpec: string; override;
  18. function GetFileInfo(const FileName: string; out FileInfo: TvfsFileInfo): Boolean; override;
  19. function Open(const FileName: string; const OpenMode: TvfsFileOpenMode; out Stream: IStreamHandle): boolean; override;
  20. function Rename(const OldName, NewName: string): boolean; override;
  21. function Delete(const aName: String): boolean; override;
  22. procedure DirectoryIndex(AddFunction: TvfsDirectoryAddFunc; List: TvfsDirectoryList; Path: string; Subdirs: boolean); override;
  23. function ZipFilename: string;
  24. end;
  25. implementation
  26. { TvfsZipArchive }
  27. constructor TvfsZipArchive.Create(const FileSpec: string; const ExtendedData: string);
  28. var
  29. path: String;
  30. dir: String;
  31. begin
  32. inherited;
  33. FZipActionCS := TCriticalSection.Create;
  34. FZipper:= TKAZip.Create(nil);
  35. path := vfsSystemPathDelim(FileSpec);
  36. if not FileExists(path) then begin
  37. dir := ExtractFilePath(path);
  38. if (dir <> '') then
  39. ForceDirectories(dir);
  40. FZipper.CreateZip(path);
  41. end;
  42. FZipper.Open(path);
  43. FZipper.CompressionType:= ctNone;
  44. end;
  45. destructor TvfsZipArchive.Destroy;
  46. begin
  47. if Assigned(FZipper) then begin
  48. FZipActionCS.Acquire;
  49. try
  50. FreeAndNil(FZipper);
  51. finally
  52. FZipActionCS.Release;
  53. end;
  54. end;
  55. FreeAndNil(FZipActionCS);
  56. inherited;
  57. end;
  58. class function TvfsZipArchive.StorageName: string;
  59. begin
  60. Result:= 'zip';
  61. end;
  62. function TvfsZipArchive.StorageGetFileSpec: string;
  63. begin
  64. Result:= Self.ZipFileName;
  65. end;
  66. procedure TvfsZipArchive.DirectoryIndex(AddFunction: TvfsDirectoryAddFunc; List: TvfsDirectoryList; Path: string; Subdirs: boolean);
  67. var
  68. i: integer;
  69. a: TvfsDirectoryEntry;
  70. e: TKAZipEntriesEntry;
  71. fp: string;
  72. dirOk: boolean;
  73. begin
  74. FZipActionCS.Acquire;
  75. try
  76. for i:= 0 to FZipper.Entries.Count-1 do begin
  77. e:= FZipper.Entries.Items[i];
  78. fp:= FZipper.GetFilePath(e.FileName);
  79. if Subdirs then
  80. dirOk:= 0=AnsiCompareStr(Path, Copy(fp, 1, Length(Path)))
  81. else
  82. dirOk:= 0=AnsiCompareFileName(Path, fp);
  83. if dirOk then begin
  84. a:= TvfsDirectoryEntry.Create;
  85. a.FileInfo.Size:= e.SizeUncompressed;
  86. a.FileInfo.Attributes:= e.Attributes;
  87. a.FileInfo.ModDate:= e.Date;
  88. AddFunction(fp+FZipper.GetFileName(e.FileName), a, List);
  89. end;
  90. end;
  91. finally
  92. FZipActionCS.Release;
  93. end;
  94. end;
  95. function TvfsZipArchive.GetFileInfo(const FileName: string; out FileInfo: TvfsFileInfo): Boolean;
  96. var
  97. i:integer;
  98. begin
  99. FZipActionCS.Acquire;
  100. try
  101. i:= FZipper.Entries.IndexOf(PrepareFilename(vfsSystemPathDelim(FileName)));
  102. Result:= i>=0;
  103. if Result then begin
  104. FileInfo.Size:= FZipper.Entries.Items[i].SizeUncompressed;
  105. FileInfo.Attributes:= FZipper.Entries.Items[i].Attributes;
  106. FileInfo.ModDate:= FZipper.Entries.Items[i].Date;
  107. end;
  108. finally
  109. FZipActionCS.Release;
  110. end;
  111. end;
  112. function TvfsZipArchive.Open(const FileName: string; const OpenMode: TvfsFileOpenMode; out Stream: IStreamHandle): boolean;
  113. var
  114. ms: TMemoryStream;
  115. eid: Integer;
  116. begin
  117. ms:= nil;
  118. Stream:= nil;
  119. Result:= false;
  120. try
  121. FZipActionCS.Acquire;
  122. try
  123. eid:= FZipper.Entries.IndexOf(PrepareFilename(vfsSystemPathDelim(FileName)));
  124. if (eid>=0) and (OpenMode<>omCreateAlways) then begin
  125. ms:= TMemoryStream.Create;
  126. FZipper.Entries.Items[eid].ExtractToStream(ms);
  127. end;
  128. finally
  129. FZipActionCS.Release;
  130. end;
  131. case OpenMode of
  132. omReadOnly: if Assigned(ms) then begin
  133. ms.Position:= 0;
  134. Stream:= TvfsStreamHandleRead.Create(ms);
  135. end;
  136. omReadWrite: if Assigned(ms) then begin
  137. ms.Position:= ms.Size;
  138. Stream:= TvfsStreamHandleWrite.Create(ms, @Self.Flush, PChar(FileName), 1+strlen(PChar(FileName)));
  139. end;
  140. omReadWriteCreate: begin
  141. if not Assigned(ms) then
  142. ms:= TMemoryStream.Create;
  143. ms.Position:= ms.Size;
  144. Stream:= TvfsStreamHandleWrite.Create(ms, @Self.Flush, PChar(FileName), 1+strlen(PChar(FileName)));
  145. end;
  146. omCreateAlways: begin
  147. ms:= TMemoryStream.Create;
  148. Stream:= TvfsStreamHandleWrite.Create(ms, @Self.Flush, PChar(FileName), 1+strlen(PChar(FileName)));
  149. end;
  150. end;
  151. Result:= Assigned(Stream);
  152. if not Result then
  153. FreeAndNil(ms);
  154. except
  155. FreeAndNil(ms);
  156. raise;
  157. end;
  158. end;
  159. function TvfsZipArchive.Rename(const OldName, NewName: string): boolean;
  160. begin
  161. FZipActionCS.Acquire;
  162. try
  163. if FZipper.Entries.IndexOf(OldName)>=0 then
  164. FZipper.Rename(OldName, NewName)
  165. else
  166. FZipper.RenameFolder(OldName, NewName);
  167. finally
  168. FZipActionCS.Release;
  169. end;
  170. Result:= true;
  171. end;
  172. function TvfsZipArchive.Delete(const aName: String): boolean;
  173. begin
  174. //TODO: delete file from zip
  175. Result:= false;
  176. raise Exception.Create('not yet implemented');
  177. end;
  178. procedure TvfsZipArchive.Flush(Handle: TvfsStreamHandleWrite; Data: Pointer; DataSize: integer);
  179. var s:string;
  180. begin
  181. s:= StrPas(Data);
  182. Handle.GetStream.Position:= 0;
  183. FZipActionCS.Acquire;
  184. try
  185. FZipper.AddStream(s, Handle.GetStream);
  186. finally
  187. FZipActionCS.Release;
  188. end;
  189. end;
  190. function TvfsZipArchive.PrepareFilename(const aFilename: String): String;
  191. var
  192. sl: TStringList;
  193. i: Integer;
  194. begin
  195. sl := TStringList.Create;
  196. try
  197. sl.Delimiter := PathDelim;
  198. sl.StrictDelimiter := true;
  199. sl.DelimitedText := aFilename;
  200. i := 0;
  201. while (i < sl.Count) do begin
  202. if (sl[i] = '..') and (i > 0) then begin
  203. sl.Delete(i);
  204. dec(i);
  205. sl.Delete(i);
  206. end else
  207. inc(i);
  208. end;
  209. result := sl.DelimitedText;
  210. finally
  211. FreeAndNil(sl);
  212. end;
  213. end;
  214. function TvfsZipArchive.ZipFilename: string;
  215. begin
  216. Result:= FZipper.FileName;
  217. end;
  218. initialization
  219. vfsManager.RegisterProvider(TvfsZipArchive);
  220. end.