[Delphi] Compactação de arquivos com a classe nativa TZipFile

[Delphi] Compactação de arquivos com a classe nativa TZipFile

Olá, amigos! Tudo bem?
Acredito que vocês já tiveram que usar componentes de terceiros ou utilitários externos para trabalhar com compactação de arquivos no Delphi, não é?
Bom, eles não são mais necessários. Acompanhe, neste artigo, um pequeno tutorial sobre como trabalhar com a classe TZipFile nativa do Delphi exibindo, inclusive, o andamento da compactação em uma barra de progresso!

 

Se recordo bem, na época em que trabalhava com Delphi 7, eu utilizava um componente third-party chamado TZipMaster para compactação de arquivos através da aplicação. Quando migrei o meu projeto para o Delphi XE3, decidi utilizar a ferramenta 7z (7-Zip) via linha de comando através do Delphi, principalmente para evitar a instalação de componentes de terceiros.
Porém, mesmo que o 7-Zip tenha funcionado, não me atentei que o Delphi trazia uma classe nativa para trabalhar com compactação de arquivos desde a versão XE2, chamada TZipFile. Imediatamente fiz alguns testes com essa classe e os resultados foram positivos, me incentivando a usá-la a partir daquele momento.

Na versão Seattle, a classe TZipFile recebeu o evento OnProgress, exclusivo para trabalhar com o status da compactação em tempo real, fornecendo as seguintes informações:

  • Nome do arquivo
  • Cabeçalho com informações adicionais do arquivo
  • Bytes processados

Com este evento, é possível, por exemplo, exibir a porcentagem ou uma barra de progresso enquanto os arquivos são compactados. Interessante, não?

 

Bom, melhor apresentar tudo isso na prática! Mãos à obra!
Trabalhar com a classe TZipFile é bem, bem fácil. Primeiro, é necessário declarar o namespace System.Zip na seção uses. Depois, basta instanciar um objeto da classe para definir os dados e executar a compactação:

var
  ZipFile: TZipFile;
begin
  // Cria uma instância da classe TZipFile
  ZipFile := TZipFile.Create;
  try
    // Indica o diretório e nome do arquivo Zip que será criado
    ZipFile.Open(GetCurrentDir + '\ArquivosCompactados.zip', zmWrite);
 
    // Compacta os arquivos
    ZipFile.Add('C:\Documento.docx');
    ZipFile.Add('C:\Planilha.xlsx');
    ZipFile.Add('C:\Apresentacao.pptx');
 
    MessageDlg('Compactação concluída!', mtInformation, [mbOK], 0);
  finally
    // Libera o objeto da memória
    ZipFile.Free;
  end;
end;

Pronto, pessoal! Não tem segredo, né? 🙂
Porém, não paramos por aqui. Agora, partiremos para a parte mais interessante, que são os aprimoramentos!

 

Melhoria #1: Adicionar arquivos de forma dinâmica
Claro, não deixaremos os nomes dos arquivos fixos no código-fonte. O correto é permitir que o usuário possa adicionar os arquivos que deseja compactar. Para isso, utilizaremos o componente TOpenDialog, da paleta Dialogs, configurando a propriedade Options > ofAllowMultiSelect para True, de forma que o usuário possa selecionar vários arquivos de uma só vez. Adicionaremos também um componente TListBox para apresentar os arquivos selecionados.
O código para adicionar arquivos terá a seguinte codificação:

var
  ArquivoSelecionado: string;
begin
  if OpenDialog.Execute then
  begin
    for ArquivoSelecionado in OpenDialog.Files do
      ListBox.Items.Add(ArquivoSelecionado);
  end;
end

Por conta disso, o método que realiza a compactação sofrerá apenas uma pequena alteração:

var
  ZipFile: TZipFile;
  Arquivo: string;
begin
  // Cria uma instância da classe TZipFile
  ZipFile := TZipFile.Create;
  try
    // Indica o diretório e nome do arquivo ZIP que será criado
    ZipFile.Open(GetCurrentDir + '\ArquivosCompactados.zip', zmWrite);
 
    // Percorre os arquivos adicionados na ListBox para compactá-los
    for Arquivo in ListBox.Items do
      ZipFile.Add(Arquivo);
 
    MessageDlg('Compactação concluída!', mtInformation, [mbOK], 0);
  finally
    // Libera o objeto da memória
    ZipFile.Free;
  end;
end;

 

Melhoria #2: Exibir a porcentagem de compactação do arquivo
Lembram-se do evento OnProgress? Utilizaremos ele nessa dica para fornecer um feedback visual do progresso da compactação.
O método que associaremos a este evento é do tipo TZipProcessEvent e deve obrigatoriamente obedecer à seguinte assinatura de parâmetros:

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

O primeiro parâmetro é o objeto chamador do evento que, neste caso, sempre será um objeto da classe TZipFile. O segundo parâmetro refere-se ao nome do arquivo atualmente em processo de compactação. O terceiro parâmetro é um objeto da classe TZipHeader que carrega informações de cabeçalho do arquivo, como o tamanho, CRC, método de compressão e outros dados. Por fim, o quarto parâmetro indica a quantidade de bytes que já foram processados durante a compactação.
Para o nosso exemplo, codificaremos o método abaixo no formulário, considerando que Label1 será o componente que irá exibir a porcentagem. A matemática é simples: dividimos a quantidade de bytes processados pelo tamanho total do arquivo e, em seguida, multiplicamos por 100 para encontrar a porcentagem.

private
  procedure EventoOnProgress(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64);
 
{ ... }
 
procedure TForm1.EventoOnProgress(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64);
begin
  Application.ProcessMessages;
 
  // Exibe a porcentagem de compactação do arquivo
  Label1.Caption := FormatFloat('#.## %', Position / Header.UncompressedSize * 100);
end;

Agora, claro, precisamos associar o evento no nosso objeto da classe TZipFile:

// Cria uma instância da classe TZipFile
ZipFile := TZipFile.Create;
try
  // Associa o evento OnProgress
  ZipFile.OnProgress := EventoOnProgress;
 
  { ... }

 

Melhoria #3: Exibir a porcentagem de compactação geral
Se existirem dois ou mais arquivos, a porcentagem que codificamos acima irá reiniciar para cada compactação. Seria interessante, então, se houvesse uma porcentagem para indicar o progresso geral de compactação. Pois bem, é isso que faremos!
Primeiramente, precisamos da soma do tamanho de todos os arquivos que serão compactados para usá-la no cálculo do progresso geral. Codificaremos um método que usa um objeto da classe TFileStream para acessar a função Size:

private
  function ObterTamanhoArquivo(const NomeArquivo: string): integer;
 
{ ... }
 
function TForm1.ObterTamanhoArquivo(const NomeArquivo: string): integer;
var
  StreamArquivo: TFileStream;
begin
  StreamArquivo := TFileStream.Create(NomeArquivo, fmOpenRead);
  try
    result := StreamArquivo.Size;
  finally
    StreamArquivo.Free;
  end;
end;

Em segundo lugar, será necessário também declarar duas variáveis de classe: uma para armazenar o tamanho de todos os arquivos e outra para manter a quantidade de bytes já processados. Compreendo que temos o parâmetro Position do evento OnProgress, mas lembre-se que ele é reiniciado para cada arquivo. Vale ressaltar também que essas duas variáveis são declaradas com esse escopo porque serão referenciadas em diferentes métodos.

private
  BytesParaCompactar: integer;
  BytesProcessados: cardinal;

O próximo passo é adicionar algumas linhas no método principal de compactação. Atente-se que na iteração dos itens da ListBox foi adicionada uma nova instrução para atualizar a variável BytesProcessados ao término de cada compactação. Isso é necessário para que o cálculo da porcentagem geral considere os arquivos anteriores já compactados.

var
  ZipFile: TZipFile;
  Arquivo: string;
begin
  BytesParaCompactar := 0;
  BytesProcessados := 0;
 
  // Percorre os arquivos da ListBox para encontrar o total de bytes que será compactado
  for Arquivo in ListBox.Items do
    BytesParaCompactar := BytesParaCompactar + ObterTamanhoArquivo(Arquivo);
 
  // Cria uma instância da classe TZipFile
  ZipFile := TZipFile.Create;
  try
    // Associa o evento OnProgress
    ZipFile.OnProgress := EventoOnProgress;
 
    // Indica o diretório e nome do arquivo ZIP que será criado
    ZipFile.Open(GetCurrentDir + '\ArquivosCompactados.zip', zmWrite);
 
    // Percorre os arquivos adicionados na ListBox para compactá-los
    for Arquivo in ListBox.Items do
    begin
      ZipFile.Add(Arquivo);
 
      // Atualiza a variável que armazena a quantidade de bytes processados
      BytesProcessados := BytesProcessados +
        ZipFile.FileInfo[Pred(ZipFile.FileCount)].UncompressedSize;
    end;
 
    MessageDlg('Compactação concluída!', mtInformation, [mbOK], 0);
  finally
    // Libera o objeto da memória
    ZipFile.Free;
  end;
end;

No evento OnProgress, por sua vez, também adicionaremos uma linha para atualizar o componente que exibirá a porcentagem geral, seguindo o mesmo contexto matemático.

procedure TForm1.EventoOnProgress(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64);
begin
  Application.ProcessMessages;
 
  // Exibe a porcentagem de compactação do arquivo
  Label1.Caption := FormatFloat('#.## %', Position / Header.UncompressedSize * 100);
 
  // Exibe a porcentagem de compactação geral
  Label2.Caption := FormatFloat('#.## %', (BytesProcessados + Position) / BytesParaCompactar * 100);  
end;

 

Melhoria #4: Barras de progresso!
Acharam que eu esqueceria? As barras de progresso são as mais importantes! 🙂
Navegue até a aba Win32 na paleta e adicione dois componentes TProgressBar no formulário. No código-fonte, a única alteração será no evento OnProgress para atualizar estes componentes. Além disso, já que utilizaremos os mesmos cálculos tanto para a porcentagem quanto para a barra de progresso, é mais prudente armazenar o resultado dos cálculos em variáveis:

procedure TForm1.EventoOnProgress(Sender: TObject; FileName: string; Header: TZipHeader; Position: Int64);
var
  PorcentagemArquivo: real;
  PorcentagemGeral: real;
begin
  Application.ProcessMessages;
 
  // Calcula as porcentagens de conclusão
  PorcentagemArquivo := Position / Header.UncompressedSize * 100;
  PorcentagemGeral := (BytesProcessados + Position) / BytesParaCompactar * 100;
 
  // Atualiza a exibição das porcentagens
  Label1.Caption := FormatFloat('#.## %', PorcentagemArquivo);
  Label2.Caption := FormatFloat('#.## %', PorcentagemGeral);
 
  // Atualiza as barras de progresso
  ProgressBar1.Position := Trunc(PorcentagemArquivo);
  ProgressBar2.Position := Trunc(PorcentagemGeral);
end;

 

And we’re done!
Isso é o que tenho para mostrar hoje, pessoal.
Para ajudá-los, segue abaixo o link para download do projeto de exemplo deste artigo, contendo algumas melhorias:

Download do projeto de exemplo de compactação de arquivos

 

Lembrem-se: qualquer dúvida, sugestão ou observação, deixem um comentário!
Até a próxima!


Compartilhe!
Share on FacebookTweet about this on TwitterShare on LinkedInShare on Google+Pin on PinterestEmail this to someone

9 comentários

  1. Boa noite amigão, ótimo artigo, como sempre, muito bom, tenho uma dúvida… É possível adicionar senha ao arquivo compactado?

    1. Boa noite, Danilo, como vai?
      Ótima pergunta! Até abri o Delphi para conferir. Pelo que analisei até o momento, acredito que não é possível adicionar senha com essa classe nativa, mas vou me aprofundar mais antes de dar uma resposta definitiva.

      Abraço!

    2. Também não encontrei, mas vamos que vamos… Abraços e obrigado pela breve resposta

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Preencha o campo abaixo * Time limit is exhausted. Please reload CAPTCHA.