Reputation: 3033
When you try to read a TZipFile.FileComment the compiler reports that this will not compile: [dcc64 Error] Unit1.pas(451): E2010 Incompatible types: 'string' and 'System.TArray'
var
iFileComment: string;
...
iFileComment := iZipFile.FileInfo[i].FileComment;
So... how do you read a TZipFile comment and assign it to a string? This seems strange because I can successfully write the zipfile comment as follows:
iZipFile.FileComment[ListView1.ItemIndex] := FileComment1.Text;
If you try to read the comment with:
iFileComment := iZipFile.FileComment[i];
every file in the zip file has the same comment, even though only one file actually has a filecomment.
The comments are added by this:
procedure TForm1.SetFileComment1Click(Sender: TObject); var
iZipFile: TZipFile; iListItem: TlistItem;
begin
if ListView1.ItemIndex <> -1 then
begin
iZipFile := TZipFile.Create;
try
{ Open zip file for writing }
iZipFile.Open(ZipFilename1.Text, zmReadWrite);
iZipFile.FileComment[ListView1.ItemIndex] := FileComment1.Text;
{ Close the zip file }
iZipFile.Close;
{ Update the listview }
ListView1.Items.BeginUpdate;
try
iListItem := ListView1.Items.Item[ListView1.ItemIndex];
iListItem.SubItems[5] := FileComment1.Text;
finally
ListView1.Items.EndUpdate;
end;
finally
iZipFile.Free;
end;
end
else
begin
MessageBox(0, 'A filename is not selected. Please select a filename.',
'Warning', MB_ICONINFORMATION or MB_OK);
end;
end;
EDIT
I am now setting and writing the filecomments with iZipFile.FileComment[Index] as suggested. When I set one file's comment and open the zip file in a working zip app only the single file that I set appears with the comment... In this TZipFile project when I load the same zip file, every file has the same comment but I can not see why. All the other fields operate as you would expect except for the filecomment field.
Here is the code that opens the zip file:
procedure TForm1.Open1Click(Sender: TObject);
{ Open zip file. }
var
i: integer;
iZipFile: TZipFile;
iFilename: string;
iDateTime: TDateTime;
iCompressedSize: cardinal;
iUnCompressedSize: cardinal;
iCRC32: cardinal;
iCompressionMethod: word;
iFileComment: string;
iListItem: TlistItem;
begin
if OpenDialog1.Execute then
begin
if FileExists(OpenDialog1.FileName) then
begin
iZipFile := TZipFile.Create;
try
ListView1.Items.Clear;
ZipFilename1.Text := OpenDialog1.FileName;
try
{ Open zip file for reading }
iZipFile.Open(ZipFilename1.Text, zmReadWrite);
for i := 0 to iZipFile.FileCount - 1 do
begin
iFilename := iZipFile.FileNames[i];
iListItem := ListView1.Items.Add;
iListItem.Caption := iFilename;
iDateTime := FileDateToDateTime
(iZipFile.FileInfo[i].ModifiedDateTime);
iListItem.SubItems.Add(DateTimeToStr(iDateTime)); { 0 }
iCompressedSize := iZipFile.FileInfo[i].CompressedSize;
iListItem.SubItems.Add(FormatByteSize(iCompressedSize)); { 1 }
iUnCompressedSize := iZipFile.FileInfo[i].UncompressedSize;
iListItem.SubItems.Add(FormatByteSize(iUnCompressedSize)); { 2 }
iCRC32 := iZipFile.FileInfo[i].CRC32;
iListItem.SubItems.Add(IntToStr(iCRC32)); { 3 }
iCompressionMethod := iZipFile.FileInfo[i].CompressionMethod;
iListItem.SubItems.Add
(ZipCompressionToStr(iCompressionMethod)); { 4 }
iFileComment := iZipFile.FileComment[i];
iListItem.SubItems.Add(iFileComment); { 5 }
end;
{ Close the zip file }
iZipFile.Close;
except
on E: Exception do
begin
ShowMessage(E.ClassName + #10#13 + E.Message);
end;
end;
finally
iZipFile.Free;
end;
end;
end;
end;
Upvotes: 2
Views: 1833
Reputation: 938
Sorry to revive this old question but none of the answers actually addresses the every file in the zip file has the same comment problem.
TZipFile.ReadCentralHeader
When a zip file is opened ReadCentralHeader
reads all the zip directory entries into an internal list of records but fails to clear the temporary record, being used to populate the list, between each entry.
In the case of the file comment ReadCentralHeader
first reads the FileCommentLength
value but if this value is zero then it doesn't set the value of the FileComment
value which therefore retains the value of the previous entry.
A possible work around is to take the FileCommentLength
value into account when reading the FileComment[]
property:
Comment := Copy(ZipFile.FileComment[Index], 1, ZipFile.FileInfo[Index].FileCommentLength);
Upvotes: 0
Reputation: 125729
You read it back the same way you write it, in reverse:
// Write the comment
iZipFile.FileComment[Index] := 'This is a zip file comment';
// Read it back
sComment := iZipFile.FileComment[Index];
WriteLn(sComment); // Outputs "This is a zip file comment"
Index
would match the index of the individual file within the zip archive. In other words, the first file would be at index 0, the second at index 1, etc., wit the last being at index FileCount - 1
.
Here's a working example. I created a dummy zip file named Test.zip
containing a few old .png files I've used in answers here (as seen in the image below):
I then used the below code to add comments to each file and read them back, writing them to the console:
program Project1;
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.Zip;
var
Zip: TZipFile;
i: Integer;
begin
Zip := TZipFile.Create;
Zip.Open('D:\TempFiles\Test.zip', zmReadWrite);
for i := 0 to Zip.FileCount - 1 do
Zip.FileComment[i] := Format('This is file %d', [i]);
Zip.Free; // Flushes changes to disk automatically
// Create a new instance, just to make it clear.
Zip := TZipFile.Create;
Zip.Open('D:\TempFiles\Test.zip', zmReadWrite);
for i := 0 to Zip.FileCount - 1 do
WriteLn(Zip.FileNames[i] + ':'#9 + Zip.FileComment[i]);
Zip.Free;
ReadLn;
end.
Here's the output it produced:
Here's the zip file comments it added, as displayed in 7Zip:
Upvotes: 4
Reputation: 597215
In XE2, TZipHeader.FileComment
was declared as a RawByteString
, which could be assigned directly to a String
(using a typecast to avoid a compiler warning):
iFileComment := String(iZipFile.FileInfo[i].FileComment);
However, in XE4, TZipHeader.FileComment
is declared as a TBytes
instead. You cannot directly assign a TBytes
to a String
(and vice versa), hense the compiler error.
If you look at the implementation of TZipFile.GetFileComment()
in XE4, it uses TZipFile.TBytesToString()
to convert the TBytes
to a String
. TZipFile.TBytesToString()
uses SysUtils.TEncoding.GetString()
to do the actual conversion, using either codepage 65001 or 437 depending on the value of the TZipFile.UTF8Support
property.
The correct way to get/set each file's comment is to use the TZipFile.FileComments[]
property, like Ken demonstrates. Not only does it handle the string<->byte conversions for you, but it also checks to make sure the zip is open before accessing the data.
Upvotes: 4