* Added possibility to set filename in archive, and added possibility to use streams

git-svn-id: trunk@11833 -
This commit is contained in:
michael 2008-09-28 14:35:28 +00:00
parent 50012c2357
commit 7e5340c07c

View File

@ -119,14 +119,6 @@ Const
Type
TZipItem = Class(TObject)
Path : String;
Name : String;
Size : LongInt;
DateTime : TDateTime;
HdrPos : Longint;
end;
TProgressEvent = Procedure(Sender : TObject; Const Pct : Double) of object;
TOnEndOfFileEvent = Procedure(Sender : TObject; Const Ratio : Double) of object;
TOnStartFileEvent = Procedure(Sender : TObject; Const AFileName : String) of object;
@ -260,17 +252,54 @@ Type
Class Function ZipID : Word; override;
end;
{ TZipFileEntry }
TZipFileEntry = Class(TCollectionItem)
private
FArchiveFileName: String;
FDateTime: TDateTime;
FDiskFileName: String;
FHeaderPos: Longint;
FSize: Integer;
FStream: TStream;
function GetArchiveFileName: String;
Protected
Property HdrPos : Longint Read FHeaderPos Write FheaderPos;
Public
Property Stream : TStream Read FStream Write FStream;
Published
Property ArchiveFileName : String Read GetArchiveFileName Write FArchiveFileName;
Property DiskFileName : String Read FDiskFileName Write FDiskFileName;
Property Size : Integer Read FSize Write FSize;
Property DateTime : TDateTime Read FDateTime Write FDateTime;
end;
{ TZipFileEntries }
TZipFileEntries = Class(TCollection)
private
function GetZ(AIndex : Integer): TZipFileEntry;
procedure SetZ(AIndex : Integer; const AValue: TZipFileEntry);
Public
Function AddFileEntry(Const ADiskFileName : String): TZipFileEntry;
Function AddFileEntry(Const ADiskFileName, AArchiveFileName : String): TZipFileEntry;
Function AddFileEntry(Const AStream : TSTream; Const AArchiveFileName : String): TZipFileEntry;
Property Entries[AIndex : Integer] : TZipFileEntry Read GetZ Write SetZ; default;
end;
{ TZipper }
TZipper = Class(TObject)
Private
FEntries: TZipFileEntries;
FZipping : Boolean;
FBufSize : LongWord;
FFileName : String; { Name of resulting Zip file }
FFiles : TStrings;
FInMemSize : Integer;
FOutFile : TFileStream;
FInFile : TFileStream; { I/O file variables }
FInFile : TStream; { I/O file variables }
LocalHdr : Local_File_Header_Type;
CentralHdr : Central_File_Header_Type;
EndHdr : End_of_Central_Dir_Type;
@ -278,25 +307,28 @@ Type
FOnProgress : TProgressEvent;
FOnEndOfFile : TOnEndOfFileEvent;
FOnStartFile : TOnStartFileEvent;
function CheckEntries: Integer;
procedure SetEntries(const AValue: TZipFileEntries);
Protected
Procedure OpenOutput;
Procedure CloseOutput;
Procedure CloseInput;
Procedure StartZipFile(Item : TZipItem);
Function UpdateZipHeader(Item : TZipItem; FZip : TStream; ACRC : LongWord;AMethod : Word) : Boolean;
Procedure CloseInput(Item : TZipFileEntry);
Procedure StartZipFile(Item : TZipFileEntry);
Function UpdateZipHeader(Item : TZipFileEntry; FZip : TStream; ACRC : LongWord;AMethod : Word) : Boolean;
Procedure BuildZipDirectory;
Procedure DoEndOfFile;
Procedure ZipOneFile(Item : TZipItem); virtual;
Function OpenInput(InFileName : String) : Boolean;
Procedure ZipOneFile(Item : TZipFileEntry); virtual;
Function OpenInput(Item : TZipFileEntry) : Boolean;
Procedure GetFileInfo;
Procedure SetBufSize(Value : LongWord);
Procedure SetFileName(Value : String);
Function CreateCompressor(Item : TZipItem; AinFile,AZipStream : TStream) : TCompressor; virtual;
Function CreateCompressor(Item : TZipFileEntry; AinFile,AZipStream : TStream) : TCompressor; virtual;
Public
Constructor Create;
Destructor Destroy;override;
Procedure ZipAllFiles; virtual;
Procedure ZipFiles(AFileName : String; FileList : TStrings);
Procedure ZipFiles(AFileName : String; Entries : TZipFileEntries);
Procedure Clear;
Public
Property BufferSize : LongWord Read FBufSize Write SetBufSize;
@ -307,6 +339,7 @@ Type
Property FileName : String Read FFileName Write SetFileName;
Property Files : TStrings Read FFiles;
Property InMemSize : Integer Read FInMemSize Write FInMemSize;
Property Entries : TZipFileEntries Read FEntries Write SetEntries;
end;
{ TYbZipper }
@ -319,8 +352,8 @@ Type
FBufSize : LongWord;
FFileName : String; { Name of resulting Zip file }
FOutputPath : String;
FEntries : TZipFileEntries;
FFiles : TStrings;
FZipEntries : TFPList; { don't use TFPObjectList, becuase of Contnrs dependency }
FOutFile : TFileStream;
FZipFile : TFileStream; { I/O file variables }
LocalHdr : Local_File_Header_Type;
@ -335,15 +368,15 @@ Type
Procedure OpenInput;
Procedure CloseOutput;
Procedure CloseInput;
Procedure ReadZipHeader(Item : TZipItem; out ACRC : LongWord;out AMethod : Word);
Procedure ReadZipHeader(Item : TZipFileEntry; out ACRC : LongWord;out AMethod : Word);
Procedure ReadZipDirectory;
Procedure DoEndOfFile;
Procedure UnZipOneFile(Item : TZipItem); virtual;
Procedure UnZipOneFile(Item : TZipFileEntry); virtual;
Function OpenOutput(OutFileName : String) : Boolean;
Procedure SetBufSize(Value : LongWord);
Procedure SetFileName(Value : String);
Procedure SetOutputPath(Value:String);
Function CreateDeCompressor(Item : TZipItem; AMethod : Word;AZipFile,AOutFile : TStream) : TDeCompressor; virtual;
Function CreateDeCompressor(Item : TZipFileEntry; AMethod : Word;AZipFile,AOutFile : TStream) : TDeCompressor; virtual;
Public
Constructor Create;
Destructor Destroy;override;
@ -360,6 +393,7 @@ Type
Property FileName : String Read FFileName Write SetFileName;
Property OutputPath : String Read FOutputPath Write SetOutputPath;
Property Files : TStrings Read FFiles;
Property Entries : TZipFileEntries Read FEntries Write FEntries;
end;
EZipError = Class(Exception);
@ -372,6 +406,9 @@ ResourceString
SErrInvalidCRC = 'Invalid CRC checksum while unzipping %s';
SErrCorruptZIP = 'Corrupt ZIP file %s';
SErrUnsupportedCompressionFormat = 'Unsupported compression format %d';
SErrMissingFileName = 'Missing filename in entry %d';
SErrMissingArchiveName = 'Missing archive filename in streamed entry %d';
SErrFileDoesNotExist = 'File "%s" does not exist.';
{ ---------------------------------------------------------------------
Auxiliary
@ -907,26 +944,42 @@ end;
Procedure TZipper.GetFileInfo;
Var
Info : TSearchRec;
I : Longint;
NewNode : TZipItem;
F : TZipFileEntry;
Info : TSearchRec;
I : Longint;
Begin
For I := 0 to FFiles.Count-1 do
For I := 0 to FEntries.Count-1 do
begin
If FindFirst(FFiles[I], STDATTR, Info)=0 then
try
NewNode:=TZipItem.Create;
NewNode.Path := ExtractFilePath(FFiles[i]);
NewNode.Name := Info.Name;
NewNode.Size := Info.Size;
NewNode.DateTime:=FileDateToDateTime(Info.Time);
FFiles.Objects[i]:=NewNode;
finally
FindClose(Info);
end;
end;
F:=FEntries[i];
If F.Stream=Nil then
begin
If (F.DiskFileName='') then
Raise EZipError.CreateFmt(SErrMissingFileName,[I]);
If FindFirst(F.DiskFileName, STDATTR, Info)=0 then
try
F.Size:=Info.Size;
F.DateTime:=FileDateToDateTime(Info.Time);
finally
FindClose(Info);
end
else
Raise EZipError.CreateFmt(SErrFileDoesNotExist,[F.DiskFileName]);
end
else
begin
If (F.ArchiveFileName='') then
Raise EZipError.CreateFmt(SErrMissingArchiveName,[I]);
F.Size:=F.Stream.Size;
end;
end;
end;
procedure TZipper.SetEntries(const AValue: TZipFileEntries);
begin
if FEntries=AValue then exit;
FEntries.Assign(AValue);
end;
Procedure TZipper.OpenOutput;
@ -936,13 +989,16 @@ Begin
End;
Function TZipper.OpenInput(InFileName : String) : Boolean;
Function TZipper.OpenInput(Item : TZipFileEntry) : Boolean;
Begin
FInFile:=TFileStream.Create(InFileName,fmOpenRead);
If (Item.Stream<>nil) then
FInFile:=Item.Stream
else
FInFile:=TFileStream.Create(Item.DiskFileName,fmOpenRead);
Result:=True;
If Assigned(FOnStartFile) then
FOnStartFile(Self,InFileName);
FOnStartFile(Self,Item.ArchiveFileName);
End;
@ -953,14 +1009,17 @@ Begin
end;
Procedure TZipper.CloseInput;
Procedure TZipper.CloseInput(Item : TZipFileEntry);
Begin
FreeAndNil(FInFile);
If (FInFile<>Item.Stream) then
FreeAndNil(FInFile)
else
FinFile:=Nil;
end;
Procedure TZipper.StartZipFile(Item : TZipItem);
Procedure TZipper.StartZipFile(Item : TZipFileEntry);
Begin
FillChar(LocalHdr,SizeOf(LocalHdr),0);
@ -980,11 +1039,11 @@ Begin
End;
Function TZipper.UpdateZipHeader(Item : TZipItem; FZip : TStream; ACRC : LongWord; AMethod : Word) : Boolean;
Function TZipper.UpdateZipHeader(Item : TZipFileEntry; FZip : TStream; ACRC : LongWord; AMethod : Word) : Boolean;
var
ZFileName : ShortString;
Begin
ZFileName:=Item.Path+Item.Name;
ZFileName:=Item.ArchiveFileName;
With LocalHdr do
begin
FileName_Length := Length(ZFileName);
@ -1009,11 +1068,11 @@ Var
SavePos : LongInt;
HdrPos : LongInt;
CenDirPos : LongInt;
Entries : Word;
ACount : Word;
ZFileName : ShortString;
Begin
Entries := 0;
ACount := 0;
CenDirPos := FOutFile.Position;
FOutFile.Seek(0,soFrombeginning); { Rewind output file }
HdrPos := FOutFile.Position;
@ -1039,7 +1098,7 @@ Begin
FOutFile.Seek(0,soFromEnd);
FOutFile.WriteBuffer(CentralHdr,SizeOf(CentralHdr));
FOutFile.WriteBuffer(ZFileName[1],Length(ZFileName));
Inc(Entries);
Inc(ACount);
FOutFile.Seek(SavePos + LocalHdr.Compressed_Size,soFromBeginning);
HdrPos:=FOutFile.Position;
FOutFile.ReadBuffer(LocalHdr, SizeOf(LocalHdr));
@ -1051,8 +1110,8 @@ Begin
Signature := END_OF_CENTRAL_DIR_SIGNATURE;
Disk_Number := 0;
Central_Dir_Start_Disk := 0;
Entries_This_Disk := Entries;
Total_Entries := Entries;
Entries_This_Disk := ACount;
Total_Entries := ACount;
Central_Dir_Size := FOutFile.Size-CenDirPos;
Start_Disk_Offset := CenDirPos;
ZipFile_Comment_Length := 0;
@ -1060,13 +1119,13 @@ Begin
end;
end;
Function TZipper.CreateCompressor(Item : TZipItem; AInFile,AZipStream : TStream) : TCompressor;
Function TZipper.CreateCompressor(Item : TZipFileEntry; AInFile,AZipStream : TStream) : TCompressor;
begin
Result:=TDeflater.Create(AinFile,AZipStream,FBufSize);
end;
Procedure TZipper.ZipOneFile(Item : TZipItem);
Procedure TZipper.ZipOneFile(Item : TZipFileEntry);
Var
CRC : LongWord;
@ -1075,7 +1134,7 @@ Var
TmpFileName : String;
Begin
OpenInput(Item.Path+Item.Name);
OpenInput(Item);
Try
StartZipFile(Item);
If (FInfile.Size<=FInMemSize) then
@ -1111,32 +1170,28 @@ Begin
DeleteFile(TmpFileName);
end;
Finally
CloseInput;
CloseInput(Item);
end;
end;
Procedure TZipper.ZipAllFiles;
Var
Item : TZipItem;
I : Integer;
filecnt : integer;
Begin
if FFiles.Count=0 then
exit;
If CheckEntries=0 then
Exit;
FZipping:=True;
Try
GetFileInfo;
OpenOutput;
Try
filecnt:=0;
For I:=0 to FFiles.Count-1 do
For I:=0 to FEntries.Count-1 do
begin
Item:=FFiles.Objects[i] as TZipItem;
if assigned(Item) then
begin
ZipOneFile(Item);
inc(filecnt);
end;
ZipOneFile(FEntries[i]);
inc(filecnt);
end;
if filecnt>0 then
BuildZipDirectory;
@ -1174,6 +1229,13 @@ begin
ZipAllFiles;
end;
procedure TZipper.ZipFiles(AFileName: String; Entries: TZipFileEntries);
begin
FFileName:=AFileName;
FEntries.Assign(Entries);
ZipAllFiles;
end;
Procedure TZipper.DoEndOfFile;
Var
@ -1194,18 +1256,32 @@ begin
FBufSize:=DefaultBufSize;
FInMemSize:=DefaultInMemSize;
FFiles:=TStringList.Create;
TStringlist(FFiles).Sorted:=True;
FEntries:=TZipFileEntries.Create(TZipFileEntry);
FOnPercent:=1;
end;
Procedure TZipper.Clear;
Function TZipper.CheckEntries : Integer;
Var
I : Integer;
begin
For I:=0 to FFiles.Count-1 do
FFiles.Objects[i].Free;
If (FFiles.Count>0) and (FEntries.Count=0) then
begin
FEntries.Clear;
For I:=0 to FFiles.Count-1 do
begin
FEntries.AddFileEntry(FFiles[i]);
end;
end;
Result:=FEntries.Count;
end;
Procedure TZipper.Clear;
begin
FEntries.Clear;
FFiles.Clear;
end;
@ -1213,6 +1289,7 @@ Destructor TZipper.Destroy;
begin
Clear;
FreeAndNil(FEntries);
FreeAndNil(FFiles);
Inherited;
end;
@ -1254,20 +1331,27 @@ Begin
end;
Procedure TUnZipper.ReadZipHeader(Item : TZipItem; out ACRC : LongWord; out AMethod : Word);
Procedure TUnZipper.ReadZipHeader(Item : TZipFileEntry; out ACRC : LongWord; out AMethod : Word);
Var
S : String;
D : TDateTime;
Begin
FZipFile.Seek(Item.HdrPos,soFromBeginning);
FZipFile.ReadBuffer(LocalHdr,SizeOf(LocalHdr));
With LocalHdr do
begin
SetLength(Item.Name,Filename_Length);
FZipFile.ReadBuffer(Item.Name[1],Filename_Length);
FZipFile.Seek(Extra_Field_Length,soCurrent);
Item.Size:=Uncompressed_Size;
ZipDateTimeToDateTime(Last_Mod_Date,Last_Mod_Time,Item.DateTime);
ACrc:=Crc32;
AMethod:=Compress_method;
SetLength(S,Filename_Length);
FZipFile.ReadBuffer(S[1],Filename_Length);
FZipFile.Seek(Extra_Field_Length,soCurrent);
Item.ArchiveFileName:=S;
Item.DiskFileName:=S;
Item.Size:=Uncompressed_Size;
ZipDateTimeToDateTime(Last_Mod_Date,Last_Mod_Time,D);
Item.DateTime:=D;
ACrc:=Crc32;
AMethod:=Compress_method;
end;
End;
@ -1275,41 +1359,43 @@ End;
Procedure TUnZipper.ReadZipDirectory;
Var
i,
EndHdrPos,
CenDirPos : LongInt;
NewNode : TZipItem;
i,
EndHdrPos,
CenDirPos : LongInt;
NewNode : TZipFileEntry;
S : String;
Begin
EndHdrPos:=FZipFile.Size-SizeOf(EndHdr);
if EndHdrPos < 0 then
raise EZipError.CreateFmt(SErrCorruptZIP,[FZipFile.FileName]);
FZipFile.Seek(EndHdrPos,soFromBeginning);
FZipFile.ReadBuffer(EndHdr, SizeOf(EndHdr));
With EndHdr do
begin
if Signature <> END_OF_CENTRAL_DIR_SIGNATURE then
raise EZipError.CreateFmt(SErrCorruptZIP,[FZipFile.FileName]);
CenDirPos:=Start_Disk_Offset;
end;
FZipFile.Seek(CenDirPos,soFrombeginning);
for i:=0 to EndHdr.Entries_This_Disk-1 do
begin
FZipFile.ReadBuffer(CentralHdr, SizeOf(CentralHdr));
With CentralHdr do
begin
if Signature<>CENTRAL_FILE_HEADER_SIGNATURE then
raise EZipError.CreateFmt(SErrCorruptZIP,[FZipFile.FileName]);
NewNode:=TZipItem.Create;
NewNode.HdrPos := Local_Header_Offset;
SetLength(NewNode.Name,Filename_Length);
FZipFile.ReadBuffer(NewNode.Name[1],Filename_Length);
FZipFile.Seek(Extra_Field_Length+File_Comment_Length,soCurrent);
FZipEntries.Add(NewNode);
end;
end;
EndHdrPos:=FZipFile.Size-SizeOf(EndHdr);
if EndHdrPos < 0 then
raise EZipError.CreateFmt(SErrCorruptZIP,[FZipFile.FileName]);
FZipFile.Seek(EndHdrPos,soFromBeginning);
FZipFile.ReadBuffer(EndHdr, SizeOf(EndHdr));
With EndHdr do
begin
if Signature <> END_OF_CENTRAL_DIR_SIGNATURE then
raise EZipError.CreateFmt(SErrCorruptZIP,[FZipFile.FileName]);
CenDirPos:=Start_Disk_Offset;
end;
FZipFile.Seek(CenDirPos,soFrombeginning);
for i:=0 to EndHdr.Entries_This_Disk-1 do
begin
FZipFile.ReadBuffer(CentralHdr, SizeOf(CentralHdr));
With CentralHdr do
begin
if Signature<>CENTRAL_FILE_HEADER_SIGNATURE then
raise EZipError.CreateFmt(SErrCorruptZIP,[FZipFile.FileName]);
NewNode:=FEntries.Add as TZipFileEntry;
NewNode.HdrPos := Local_Header_Offset;
SetLength(S,Filename_Length);
FZipFile.ReadBuffer(S[1],Filename_Length);
NewNode.ArchiveFileName:=S;
FZipFile.Seek(Extra_Field_Length+File_Comment_Length,soCurrent);
end;
end;
end;
Function TUnZipper.CreateDeCompressor(Item : TZipItem; AMethod : Word;AZipFile,AOutFile : TStream) : TDeCompressor;
Function TUnZipper.CreateDeCompressor(Item : TZipFileEntry; AMethod : Word;AZipFile,AOutFile : TStream) : TDeCompressor;
begin
case AMethod of
8 :
@ -1319,7 +1405,7 @@ begin
end;
end;
Procedure TUnZipper.UnZipOneFile(Item : TZipItem);
Procedure TUnZipper.UnZipOneFile(Item : TZipFileEntry);
Var
Count : Longint;
@ -1329,7 +1415,7 @@ Var
Begin
Try
ReadZipHeader(Item,CRC,ZMethod);
OutputFileName:=Item.Name;
OutputFileName:=Item.DiskFileName;
if FOutputPath<>'' then
OutputFileName:=IncludeTrailingPathDelimiter(FOutputPath)+OutputFileName;
OpenOutput(OutputFileName);
@ -1345,7 +1431,7 @@ Begin
OnPercent:=Self.OnPercent;
DeCompress;
if CRC<>Crc32Val then
raise EZipError.CreateFmt(SErrInvalidCRC,[Item.Name]);
raise EZipError.CreateFmt(SErrInvalidCRC,[Item.ArchiveFileName]);
Finally
Free;
end;
@ -1357,9 +1443,10 @@ end;
Procedure TUnZipper.UnZipAllFiles;
Var
Item : TZipItem;
Item : TZipFileEntry;
I : Integer;
AllFiles : Boolean;
Begin
FUnZipping:=True;
Try
@ -1367,15 +1454,14 @@ Begin
OpenInput;
Try
ReadZipDirectory;
For I:=0 to FZipEntries.Count-1 do
For I:=0 to FEntries.Count-1 do
begin
Item:=TZipItem(FZipEntries[i]);
if AllFiles or
(FFiles.IndexOf(Item.Name)<>-1) then
UnZipOneFile(Item);
Item:=FEntries[i];
if AllFiles or (FFiles.IndexOf(Item.ArchiveFileName)<>-1) then
UnZipOneFile(Item);
end;
Finally
CloseInput;
CloseInput;
end;
finally
FUnZipping:=False;
@ -1441,23 +1527,16 @@ Constructor TUnZipper.Create;
begin
FBufSize:=DefaultBufSize;
FFiles:=TStringList.Create;
FZipEntries:=TFPList.Create;
TStringlist(FFiles).Sorted:=True;
FEntries:=TZipFileEntries.Create(TZipFileEntry);
FOnPercent:=1;
end;
Procedure TUnZipper.Clear;
Var
I : Integer;
begin
For I:=0 to FFiles.Count-1 do
FFiles.Objects[i].Free;
FFiles.Clear;
For I:=0 to FZipEntries.Count-1 do
TZipItem(FZipEntries[i]).Free;
FZipEntries.Clear;
FEntries.Clear;
end;
Destructor TUnZipper.Destroy;
@ -1465,8 +1544,51 @@ Destructor TUnZipper.Destroy;
begin
Clear;
FreeAndNil(FFiles);
FreeAndNil(FZipEntries);
FreeAndNil(FEntries);
Inherited;
end;
{ TZipFileEntry }
function TZipFileEntry.GetArchiveFileName: String;
begin
Result:=FArchiveFileName;
If (Result='') then
Result:=FDiskFileName;
end;
{ TZipFileEntries }
function TZipFileEntries.GetZ(AIndex : Integer): TZipFileEntry;
begin
Result:=TZipFileEntry(Items[AIndex]);
end;
procedure TZipFileEntries.SetZ(AIndex : Integer; const AValue: TZipFileEntry);
begin
Items[AIndex]:=AValue;
end;
function TZipFileEntries.AddFileEntry(const ADiskFileName: String
): TZipFileEntry;
begin
Result:=Add as TZipFileEntry;
Result.DiskFileName:=ADiskFileName;
end;
function TZipFileEntries.AddFileEntry(const ADiskFileName,
AArchiveFileName: String): TZipFileEntry;
begin
Result:=AddFileEntry(ADiskFileName);
Result.ArchiveFileName:=AArchiveFileName;
end;
function TZipFileEntries.AddFileEntry(const AStream: TSTream;
const AArchiveFileName: String): TZipFileEntry;
begin
Result:=Add as TZipFileEntry;
Result.Stream:=AStream;
Result.ArchiveFileName:=AArchiveFileName;
end;
End.