Watch, Follow, &
Connect with Us

Please visit our new home
community.embarcadero.com.


Welcome, Guest
Guest Settings
Help

Thread: ZLib Multiple Files


This question is answered.


Permlink Replies: 6 - Last Post: Feb 28, 2015 5:50 AM Last Post By: André Diel
André Diel

Posts: 27
Registered: 7/28/07
ZLib Multiple Files  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 27, 2015 11:48 AM
Hi,

I use Delphi XE5.
And I'm unable to generate the compression of multiple files, it loses a piece of the name at the time of decompression.

Does anyone have any routine or tip?

Thanks.
Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: ZLib Multiple Files
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 27, 2015 12:38 PM   in response to: André Diel in response to: André Diel
André wrote:

I use Delphi XE5.
And I'm unable to generate the compression of multiple files

And how are you trying to compress multiple files exactly? Please show your
actual code.

it loses a piece of the name at the time of decompression.

What name are you referring to? Youneed to be more specific.

--
Remy Lebeau (TeamB)
Just JJ

Posts: 44
Registered: 8/21/14
Re: ZLib Multiple Files  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 27, 2015 12:52 PM   in response to: André Diel in response to: André Diel
On Fri, 27 Feb 2015 11:48:38 -0800, André Diel wrote:
Hi,

I use Delphi XE5.
And I'm unable to generate the compression of multiple files, it loses a piece of the name at the time of decompression.

Does anyone have any routine or tip?

Thanks.

I could only guess that the de/compression of the multiple files ad their
file names are not handled properly because they aren't part of ZLib.

You should debug your code to make sure it properly construct your archive
data before compressing them, and make sure it properly read the
uncompressed data.
André Diel

Posts: 27
Registered: 7/28/07
Re: ZLib Multiple Files  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 27, 2015 1:07 PM   in response to: André Diel in response to: André Diel
Hello, thanks for replying,

I lose a piece on the name and the file extension.
example:
Original name = "Temp_Log.txt '
After decompression, 'Temp_L';

Those are my codes.
procedure CompressFiles(Files: TStrings; const Compressed: String);
var
  FileInName: TFileName;
  FileIn, FileOut: TFileStream;
  Compressor: TCompressionStream;
  NumArq, I, Len, Size: Integer;
  Fim: Byte;
begin
  FileOut := TFileStream.Create(Compressed, fmCreate or fmShareExclusive);
  Compressor := TCompressionStream.Create(clMax, FileOut);
  NumArq := Files.Count;
  Compressor.Write(NumArq, SizeOf(Integer));
  try
    for I := 0 to (Files.Count - 1) do
    begin
      FileIn := TFileStream.Create(Files[I], fmOpenRead and fmShareExclusive);
      try
        FileInName := ExtractFileName(Files[I]);
        Len := Length(FileInName);
        Compressor.Write(Len, SizeOf(Integer));
        Compressor.Write(FileInName[1], Len);
        Size := FileIn.Size;
        Compressor.Write(Size, SizeOf(Integer));
        Compressor.CopyFrom(FileIn, FileIn.Size);
        Fim := 0;
        Compressor.Write(Fim, SizeOf(Byte));
      finally
        FileIn.Free;
      end;
    end;
  finally
    FreeAndNil(Compressor);
    FreeAndNil(FileOut);
  end;
end; 
 
{#######################################################}
 
procedure Descomprimir(ArqOrigem, ArqDestino: string);
var
  NomeSaida: string;
  FileEntrada, FileOut: TFileStream;
  Descompressor: TDecompressionStream;
  NumArq, I, Len, Size: Integer;
  Fim: Byte;
begin
  FileEntrada := TFileStream.Create(ArqOrigem, fmOpenRead and fmShareExclusive);
  Descompressor := TDecompressionStream.Create(FileEntrada);
  Descompressor.Read(NumArq, SizeOf(Integer));
  try
    I := 0;
    while I < NumArq do begin
      Descompressor.Read(Len, SizeOf(Integer));
      SetLength(NomeSaida, Len);
      Descompressor.Read(NomeSaida[1], Len);
      Descompressor.Read(Size, SizeOf(Integer));
 
      FileOut := TFileStream.Create(
        IncludeTrailingBackslash(ArqDestino) + NomeSaida,
        fmCreate or fmShareExclusive);
      try
        FileOut.CopyFrom(Descompressor, Size);
      finally
        FileOut.Free;
      end;
      Descompressor.Read(Fim, SizeOf(Byte));
      Inc(I);
    end;
  finally
    FreeAndNil(Descompressor);
    FreeAndNil(FileEntrada);
  end;
end;
 
Linden ROTH

Posts: 467
Registered: 11/3/11
Re: ZLib Multiple Files
Helpful
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 27, 2015 1:31 PM   in response to: André Diel in response to: André Diel
André Diel wrote:
Hello, thanks for replying,

I lose a piece on the name and the file extension.
example:
Original name = "Temp_Log.txt '
After decompression, 'Temp_L';

        Len := Length(FileInName);

Simple answer
        Len := Length(FileInName) * 2;

FileInName is Unicode
while Len is number of bytes
while Length( FileInName) is number of chars

AND

Number of byte is not = number of chars


--
Linden
"Mango" was Cool but "Wasabi" was Hotter but remember it's all in the "source"

Remy Lebeau (Te...


Posts: 9,447
Registered: 12/23/01
Re: ZLib Multiple Files
Correct
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 27, 2015 2:11 PM   in response to: André Diel in response to: André Diel
André rote:

I lose a piece on the name and the file extension.

Original name = "Temp_Log.txt '
After decompression, 'Temp_L';

The original string is 12 characters. The decompressed string is 6 characters.
1/2 the length. Your code is assuming SizeOf(Char) = SizeOf(Byte), and
that is not the case in D2009 and later. So you are writing only 1/2 the
filename to your compressed file. There are also some other minor bugs in
your code (mainly when handling files that are 0 bytes in size).

So you need to either:

1) update CompressFiles() to multiply Length(FileInName) by SizeOf(Char)
when writing FileInName, and update Descomprimir() to divide Len by SizeOf(Char)
before calling SetLength():

procedure CompressFiles(Files: TStrings; const Compressed: String);
var
  FileInName: String;
  FileIn, FileOut: TFileStream;
  Compressor: TCompressionStream;
  NumArq, I, Len, Size: Integer;
  Fim: Byte;
begin
  FileOut := TFileStream.Create(Compressed, fmCreate or fmShareExclusive);
  try
    Compressor := TCompressionStream.Create(clMax, FileOut);
    try
      NumArq := Files.Count;
      Compressor.WriteBuffer(NumArq, SizeOf(Integer));
      for I := 0 to (NumArq - 1) do
      begin
        FileIn := TFileStream.Create(Files[I], fmOpenRead and fmShareExclusive);
        try
          FileInName := ExtractFileName(Files[I]);
          Len := Length(FileInName) * SizeOf(Char);
          Compressor.WriteBuffer(Len, SizeOf(Integer));
          Compressor.WriteBuffer(PChar(FileInName)^, Len);
          Size := FileIn.Size;
          Compressor.WriteBuffer(Size, SizeOf(Integer));
          Compressor.CopyFrom(FileIn, Size);
          Fim := 0;
          Compressor.WriteBuffer(Fim, SizeOf(Byte));
        finally
          FileIn.Free;
        end;
      end;
    finally
      FreeAndNil(Compressor);
    end;
  finally
    FreeAndNil(FileOut);
  end;
end;
 
procedure Descomprimir(ArqOrigem, ArqDestino: string);
var
  NomeSaida: string;
  FileEntrada, FileOut: TFileStream;
  Descompressor: TDecompressionStream;
  NumArq, I, Len, Size: Integer;
  Fim: Byte;
begin
  FileEntrada := TFileStream.Create(ArqOrigem, fmOpenRead and fmShareExclusive);
  try
    Descompressor := TDecompressionStream.Create(FileEntrada);
    try
      Descompressor.ReadBuffer(NumArq, SizeOf(Integer));
      for I := 0 to (NumArq-1) do
      begin
        Descompressor.ReadBuffer(Len, SizeOf(Integer));
        SetLength(NomeSaida, Len div SizeOf(Char));
        Descompressor.ReadBuffer(PChar(NomeSaida)^, Len);
        NomeSaida := ExtractFileName(NomeSaida); // to avoid a security risk
        FileOut := TFileStream.Create(IncludeTrailingBackslash(ArqDestino) 
+ NomeSaida, fmCreate or fmShareExclusive);
        try
          Descompressor.ReadBuffer(Size, SizeOf(Integer));
          // passing 0 to CopyFrom() copies the ENTIRE input stream, which 
you DO NOT want here!
          if Size > 0 then
            FileOut.CopyFrom(Descompressor, Size);
        finally
          FileOut.Free;
        end;
        Descompressor.Read(Fim, SizeOf(Byte));
      end;
    finally
      FreeAndNil(Descompressor);
    end;
  finally
    FreeAndNil(FileEntrada);
  end;
end;

2) (I suggest this approach instead) update CompressFiles() to encode FileInName
to a byte array using TEncoding and write those bytes instead, and update
Descomprimir() to read the bytes and decode them using TEncoding:

procedure CompressFiles(Files: TStrings; const Compressed: String);
var
  FileInName: TBytes;
  FileIn, FileOut: TFileStream;
  Compressor: TCompressionStream;
  NumArq, I, Len, Size: Integer;
  Fim: Byte;
begin
  FileOut := TFileStream.Create(Compressed, fmCreate or fmShareExclusive);
  try
    Compressor := TCompressionStream.Create(clMax, FileOut);
    try
      NumArq := Files.Count;
      Compressor.WriteBuffer(NumArq, SizeOf(Integer));
      for I := 0 to (NumArq - 1) do
      begin
        FileIn := TFileStream.Create(Files[I], fmOpenRead and fmShareExclusive);
        try
          FileInName := TEncoding.UTF8.GetBytes(ExtractFileName(Files[I]));
          Len := Length(FileInName);
          Compressor.WriteBuffer(Len, SizeOf(Integer));
          Compressor.WriteBuffer(PByte(FileInName)^, Len);
          Size := FileIn.Size;
          Compressor.WriteBuffer(Size, SizeOf(Integer));
          Compressor.CopyFrom(FileIn, Size);
          Fim := 0;
          Compressor.WriteBuffer(Fim, SizeOf(Byte));
        finally
          FileIn.Free;
        end;
      end;
    finally
      FreeAndNil(Compressor);
    end;
  finally
    FreeAndNil(FileOut);
  end;
end;
 
procedure Descomprimir(ArqOrigem, ArqDestino: string);
var
  NomeSaidaBytes: TBytes;
  NomeSaida: string;
  FileEntrada, FileOut: TFileStream;
  Descompressor: TDecompressionStream;
  NumArq, I, Len, Size: Integer;
  Fim: Byte;
begin
  FileEntrada := TFileStream.Create(ArqOrigem, fmOpenRead and fmShareExclusive);
  try
    Descompressor := TDecompressionStream.Create(FileEntrada);
    try
      Descompressor.ReadBuffer(NumArq, SizeOf(Integer));
      for I := 0 to (NumArq-1) do
      begin
        Descompressor.ReadBuffer(Len, SizeOf(Integer));
        SetLength(NomeSaidaBytes, Len);
        Descompressor.ReadBuffer(PByte(NomeSaidaBytes)^, Len);
        NomeSaida := TEncoding.UTF8.GetString(NomeSaidaBytes);
        NomeSaida := ExtractFileName(NomeSaida); // to avoid a security risk
        FileOut := TFileStream.Create(IncludeTrailingBackslash(ArqDestino) 
+ NomeSaida, fmCreate or fmShareExclusive);
        try
          Descompressor.ReadBuffer(Size, SizeOf(Integer));
          // passing 0 to CopyFrom() copies the ENTIRE input stream, which 
you DO NOT want here!
          if Size > 0 then
            FileOut.CopyFrom(Descompressor, Size);
        finally
          FileOut.Free;
        end;
        Descompressor.Read(Fim, SizeOf(Byte));
      end;
    finally
      FreeAndNil(Descompressor);
    end;
  finally
    FreeAndNil(FileEntrada);
  end;
end;


--
Remy Lebeau (TeamB)
André Diel

Posts: 27
Registered: 7/28/07
Re: ZLib Multiple Files  
Click to report abuse...   Click to reply to this thread Reply
  Posted: Feb 28, 2015 5:50 AM   in response to: André Diel in response to: André Diel
Right.
It worked perfectly.

Thanks to all.
Thanks.

Below is my updated routines.

procedure CompressFiles(Files: TStrings; const Compressed: String);
var
  FileInName: TBytes;
  FileIn, FileOut: TFileStream;
  Compressor: TCompressionStream;
  NumFiles, I, Len, Size: Integer;
  Fin: Byte;
begin
  FileOut := TFileStream.Create(Compressed, fmCreate or fmShareExclusive);
  Try
    Compressor := TCompressionStream.Create(clMax, FileOut);
    Try
      NumFiles := Files.Count;
      Compressor.Write(NumFiles, SizeOf(Integer));
      for I := 0 to (NumFiles - 1) do
      begin
        FileIn := TFileStream.Create(Files[I], fmOpenRead and fmShareExclusive);
        try
          { write File Name }
          FileInName := TEncoding.UTF8.GetBytes(ExtractFileName(Files[I]));
          Len        := Length(FileInName);
          Compressor.Write(Len, SizeOf(Integer));
          Compressor.Write(PByte(FileInName)^,  Len);
 
          { Write File }
          Size := FileIn.Size;
          Compressor.Write(Size, SizeOf(Integer));
          Compressor.CopyFrom(FileIn, FileIn.Size);
          Fin := 0;
          Compressor.Write(Fin, SizeOf(Byte));
        finally
          FileIn.Free;
        end;
      end;
    finally
      FreeAndNil(Compressor);
    end;
  Finally
    FreeAndNil(FileOut);
  End;
end;
 
 { ############################ }
 
procedure DecompressFile(Compressed, Decompressed: String);
var
  FileNameBytes: TBytes;
  FileName: string;
  FaleIn, FileOut: TFileStream;
  Descompressor: TDecompressionStream;
  NumFiles, I, Len, Size: Integer;
  Fin: Byte;
begin
  FaleIn := TFileStream.Create(Compressed, fmOpenRead and fmShareExclusive);
  Try
    Descompressor := TDecompressionStream.Create(FaleIn);
    try
      Descompressor.ReadBuffer(NumFiles, SizeOf(Integer));
      for i := 0 to (NumFiles - 1) do
      begin
        { Read File Name }
        Descompressor.ReadBuffer(Len, SizeOf(Integer));
        SetLength(FileNameBytes, Len);
        Descompressor.ReadBuffer(PByte(FileNameBytes)^, Len);
        FileName := TEncoding.UTF8.GetString(FileNameBytes);
        FileName := ExtractFileName(FileName);
 
        { Read File }
        Descompressor.Read(Size, SizeOf(Integer));
        FileOut := TFileStream.Create(IncludeTrailingBackslash(Decompressed) + FileName, fmCreate or fmShareExclusive);
        try
          if (Size > 0) then
            FileOut.CopyFrom(Descompressor, Size);
        finally
          FileOut.Free;
        end;
        Descompressor.Read(Fin, SizeOf(Byte));
      end;
    finally
      FreeAndNil(Descompressor);
    end;
  Finally
    FreeAndNil(FaleIn);
  End;
end;
Legend
Helpful Answer (5 pts)
Correct Answer (10 pts)

Server Response from: ETNAJIVE02