• Blog
  • Compressing files with Delphi TZipFile native class

Compressing files with Delphi TZipFile native class

Сode examples of compressing and extracting files

Publish date:
    DISCOVER MORE OF WHAT MATTERS TO YOU

    Compressing files with Delphi TZipFile native class

    Overview

    It’s very likely that every developer has ever used third-party components to work with file compression in Delphi. For instance, a few years ago, a component called TZipMaster became very popular and widely applied. Other developers preferred to work with the free 7-Zip utility app, which provides command-line functions to compress files.

    Well, the good news is that they are not required anymore. Since XE2, Embarcadero has introduced a new Delphi native class to work with compression, called TZipFile, and has also been applying improvements to it over the years. One of them was released in the Seattle version. This new event allows developers to work with the compression status in real-time, providing information as:

    • Filename
    • File header with additional details
    • Bytes processed

    So, by working with this event, it’s possible to display a percentage or a progress bar while the files are being compressed. Let’s bring all of this to a hands-on example.

    Сode examples of compressing and extracting files

    TZipFile is simple and straightforward to use. First of all, the namespace System.Zip should be declared in the unit “uses” section (“interface” preferably, in accordance with the further examples), and, secondly, just declare and create an object of TZipFile class to run the compression. Have a look at the example below:

    1234567891011121314151617181920212223
    uses
    System.Zip;
    var
    ZipFile: TZipFile;
    begin
    // Creates a new instance of TZipFile
    ZipFile := TZipFile.Create;
    try
    // Defines the filename which will be created
    ZipFile.Open(GetCurrentDir + '\CompressedFiles.zip', zmWrite);
    // Compress the files
    ZipFile.Add('C:\Temp\Document.docx');
    ZipFile.Add('C:\Temp\Worksheet.xlsx');
    ZipFile.Add('C:\Temp\Presentation.pptx');
    MessageDlg('Operation completed!', mtInformation, [mbOK], 0);
    finally
    // Frees the object from memory
    ZipFile.Free;
    end;
    end;

    Likewise, extracting a compressed file is very similar:

    123456789101112131415
    var
    ZipFile: TZipFile;
    begin
    // Creates a new instance of TZipFile
    ZipFile := TZipFile.Create;
    try
    // Extracts the compressed file
    ZipFile.ExtractZipFile(GetCurrentDir + '\CompressedFiles.zip', GetCurrentDir);
    MessageDlg('Operation completed!', mtInformation, [mbOK], 0);
    finally
    // Frees the object from memory
    ZipFile.Free;
    end;
    end;

    As you can see, there is really no secret at all.

    However, that’s not everything that TZipFile provides. Let’s take a deeper look at the extended capabilities of the class.

    Adding files dynamically

    In the real world, filenames cannot be hard-coded. The application should allow the user to select the files that they want to compress. From the Dialogs palette, there is a component called TOpenDialog that can be applied for this purpose. Drop this component onto the form, and set the Options > ofAllowMultiSelect property to True, so that the user can select multiple files at once. As an improvement, a TListBox object can be used to display the selected files. The code snippet for the files selection would be similar to the one below:

    123456789
    var
    SelectedFile: string;
    begin
    if OpenDialog1.Execute then
    begin
    for SelectedFile in OpenDialog1.Files do
    ListBox1.Items.Add(SelectedFile);
    end;
    end;

    Since the process is now dynamic, the method that compresses the files should be changed:

    1234567891011121314151617181920
    var
    ZipFile: TZipFile;
    FileItem: string;
    begin
    // Creates a new instance of TZipFile
    ZipFile := TZipFile.Create;
    try
    // Defines the filename which will be created
    ZipFile.Open(GetCurrentDir + '\CompressedFiles.zip', zmWrite);
    // Reads the files added to the TListBox
    for FileItem in ListBox1.Items do
    ZipFile.Add(FileItem);
    MessageDlg('Operation completed!', mtInformation, [mbOK], 0);
    finally
    // Frees the object from memory
    ZipFile.Free;
    end;
    end;

    Displaying the file compression progress

    Do you remember the OnProgress event, mentioned at the beginning of this article? It can be used to get the number of bytes to be processed and bytes that have been already processed, so, by performing a calculation, the real-time compression progress can be displayed.

    This event accepts a method with the following definition:

    1
    procedure(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64);

    The first parameter represents the caller which, in this case, will always be an object of the TZipFile class type. The second parameter refers to the filename currently being compressed. The next parameter is an object of the TZipHeader class which carries additional information, such as header, size, CRC, compression method, and other details. Finally, the fourth parameter indicates the bytes that have already been processed during the compression process.Look at the example method below, considering that Label1 is the component that will display the percentage. The math is simple: divide the number of bytes processed by the file size and, then, multiply by 100 to find the proper percentage.

    1234567891011121314151617181920
    private
    procedure OnProgressEvent(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64);
    { … }
    procedure TForm1.OnProgressEvent(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64);
    var
    UncompressedSizePercentage: real;
    begin
    Application.ProcessMessages;
    // Initializes the percentage variables as 1, case the selected file has zero size
    UncompressedSizePercentage := 1;
    if Header.UncompressedSize > 0 then
    UncompressedSizePercentage := Position / Header.UncompressedSize;
    // Displays the compression percentage
    Label1.Caption := FormatFloat('#.## %', UncompressedSizePercentage * 100);
    end;

    Now, the final touch. You need to assign the method to the event:

    123456
    ZipFile := TZipFile.Create;
    try
    // Assigns the method to the event
    ZipFile.OnProgress := OnProgressEvent;
    { … }

    However, if the user selects two or more files, the calculation will restart for each file. From a UX point of view, it does not sound intuitive. Therefore, it would be better to display the overall compression progress.

    Displaying the overall compression progress

    The first step is to calculate the size of all files that have been selected. To do it, you can use the TFileStream class due to the Size property:

    12345678910111213141516
    private
    function GetFileSize(const FileName: string): Integer;
    { … }
    function TForm1.GetFileSize(const FileName: string): Integer;
    var
    StreamArchive: TFileStream;
    begin
    StreamArchive := TFileStream.Create(FileName, fmOpenRead);
    try
    Result := StreamArchive.Size;
    finally
    StreamArchive.Free;
    end;
    end;

    The second step is to create two class variables: one for storing the size of all files, and another one for storing the number of bytes that have been already compressed. Although there is a parameter called Position in the OnProgress event, it’s important to remind that it’s restarted for each file, hence it is necessary to use variables. Also, these variables will be used in different methods, so they need to be at class scope.

    123
    private
    BytesToProcess: Integer;
    BytesProcessed: Cardinal;

    The next step presupposes applying some changes to the main compression method to feed both variables:

    123456789101112131415161718192021222324252627282930313233343536
    var
    ZipFile: TZipFile;
    FileItem: string;
    begin
    BytesToProcess := 0;
    BytesProcessed := 0;
    // Sums the size of all selected files
    for FileItem in ListBox1.Items do
    BytesToProcess := BytesToProcess + GetFileSize(FileItem);
    // Creates a new instance of TZipFile
    ZipFile := TZipFile.Create;
    try
    // Assigns the method to the OnProgress event
    ZipFile.OnProgress := OnProgressEvent;
    // Defines the filename which will be created
    ZipFile.Open(GetCurrentDir + '\CompressedFiles.zip', zmWrite);
    // Reads the files added to the TListBox object to compress them
    for FileItem in ListBox1.Items do
    begin
    ZipFile.Add(FileItem);
    // Updates the variable that stores the number of bytes processed
    BytesProcessed := BytesProcessed +
    ZipFile.FileInfo[Pred(ZipFile.FileCount)].UncompressedSize;
    end;
    MessageDlg('Operation completed!', mtInformation, [mbOK], 0);
    finally
    // Frees the object from memory
    ZipFile.Free;
    end;
    end;

    Some changes are also required in the OnProgress event as it should display not only the compression progress of the current file but also the general compression (considering a new TLabel component called Label2):

    1234567891011121314151617181920212223
    procedure TForm1.OnProgressEvent(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64);
    var
    UncompressedSizePercentage: Real;
    BytesProcessedPercentage: Real;
    begin
    Application.ProcessMessages;
    // Initializes the percentage variables as 1, case the selected file has zero size
    UncompressedSizePercentage := 1;
    BytesProcessedPercentage := 1;
    if Header.UncompressedSize > 0 then
    UncompressedSizePercentage := Position / Header.UncompressedSize;
    if BytesProcessed > 0 then
    BytesProcessedPercentage := (BytesProcessed + Position) / BytesProcessed;
    // Displays the current file compression progress
    Label1.Caption := FileName + ': ' + FormatFloat('#.## %', UncompressedSizePercentage * 100);
    // Displays the general compression progress (all files)
    Label2.Caption := 'Overall Progress: ' + FormatFloat('#.## %', BytesProcessedPercentage * 100);
    end;

    Using progress bars to enhance the experience

    Progress bars are widely used to give feedback to the user on a specific ongoing operation. Popular compressors, such as WinRAR and 7-Zip, use progress bars to display the compression and decompression progress. The same behavior can be created in Delphi applications.Drop two TProgressBar components (from the Win32 palette) onto the form. The only change will be required for the OnProgress event where you will need to update their positions. Besides, since the same calculations will be performed for the labels and progress bars, it will be sensible to store their results in local variables.

    1234567891011121314151617181920212223242526272829
    procedure TForm1.OnProgressEvent(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64);
    var
    CurrentFileProgress: Real;
    OverallProgress: Real;
    begin
    Application.ProcessMessages;
    CurrentFileProgress := 1;
    OverallProgress := 1;
    // Calculates the progress
    if Header.UncompressedSize > 0 then
    CurrentFileProgress := Position / Header.UncompressedSize;
    if BytesToProcess > 0 then
    OverallProgress := (BytesProcessed + Position) / BytesToProcess;
    // Converts to percentages
    CurrentFileProgress := CurrentFileProgress * 100;
    OverallProgress := OverallProgress * 100;
    // Updates the labels with each progress
    Label1.Caption := FileName + ': ' + FormatFloat('#.## %', CurrentFileProgress);
    Label2.Caption := 'Overall Progress: ' + FormatFloat('#.## %', OverallProgress);
    // Updates the progress bars with each progress
    ProgressBar1.Position := Trunc(CurrentFileProgress);
    ProgressBar2.Position := Trunc(OverallProgress);
    end;

    If you need to use compression methods in your application, we recommend you consider TZipFile. At Embarcadero DocWiki, you can find detailed documentation that will help you to work with it.

    Subscribe to our newsletter and get amazing content right in your inbox.

    This field is required
    This field is required Invalid email address

    Thank you for subscribing!
    See you soon... in your inbox!

    confirm your subscription, make sure to check your promotions/spam folder

    Subscribe to our newsletter and get amazing content right in your inbox.

    You can unsubscribe from the newsletter at any time

    This field is required
    This field is required Invalid email address

    You're almost there...

    A confirmation was sent to your email

    confirm your subscription, make sure to check
    your promotions/spam folder