Compressão de Arquivos


Já agora nesta nova versão do .NET Framework, 2.0, temos um novo namespace, dentro do System.IO, chamado System.IO.Compression. Este namespace contém 2 classes chamadas DeflateStream e GZipStream. Elas tem uma interface semelhante, mas uma delas, a DeflateStream utiliza o algoritmo Deflate para comprimir e descomprimir os arquivos. Temos também um enumerador dentro deste namespace chamado CompressionMode, que indica o que será feito (Compressão ou Descompressão) com o Stream.

Para ilustrarmos isso, vamos fazer um teste utilizando a classe GZipStream, comprimindo um arquivo de 527 KB, analisando os códigos abaixo:

1
2
 
Imports System.IO
Imports System.IO.Compression
 
Código 1 – Importando os Namespaces necessários.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 
Public Sub Comprime(ByVal arquivo As String)
    Dim stream As FileStream = Nothing
    Try
        stream = New FileStream(arquivo, FileMode.Open, FileAccess.Read, FileShare.Read)
 
        Dim buffer(stream.Length – 1) As Byte
        Dim count As Integer = stream.Read(buffer, 0, buffer.Length)
        stream.Close()
 
        Dim ms As New MemoryStream()
        Dim zip As New GZipStream(ms, CompressionMode.Compress, True)
        zip.Write(buffer, 0, buffer.Length)
        zip.Close()
        WriteFile(“ArquivoCompact.gz”, ms.ToArray, ms.Length)
    Finally
        If Not (stream Is Nothing) Then
            stream.Close()
        End If
    End Try
End Sub
 
Public Sub Descomprime(ByVal arquivo As String)
    Dim stream As FileStream = Nothing
    Try
        stream = New FileStream(arquivo, FileMode.Open, FileAccess.Read, FileShare.Read)
        Dim ms As New MemoryStream(stream.Length)
        Dim unzip As New GZipStream(ms, CompressionMode.Decompress, True)
 
        Dim data(stream.Length – 1) As Byte
        stream.Read(data, 0, data.Length)
        ms.Write(data, 0, data.Length)
        ms.Position = 0
 
        Dim dataOutput() As Byte = ReadBytes(unzip)
        WriteFile(“ArquivoOriginal.exe”, dataOutput, dataOutput.Length)
        unzip.Close()
    Finally
        If Not (stream Is Nothing) Then
            stream.Close()
        End If
    End Try
End Sub
 
Public Function ReadBytes(ByVal s As Stream) As Byte()
    Dim buffer(10000) As Byte
    Dim bytesLidos As Integer = 0
    Using ms As New MemoryStream()
        Do
            bytesLidos = s.Read(buffer, 0, 10000)
            ms.Write(buffer, 0, bytesLidos)
        Loop Until bytesLidos <= 0
        Return ms.ToArray()
    End Using
End Function
 
Código 2 – Função que comprime de descomprime o arquivo informado.

Como podemos ver no código 2 logo acima, utilizamos a classe GZipStream tanto para comprimir quanto para descomprimir o arquivo do nosso exemplo. Para o nome do arquivo que é passado para a função “Comprime”, resgatamos o mesmo para um FileStream. Com isso, conseguimos manipulá-lo e passarmos para a classe de compressão fazer o trabalho e gerar o arquivo em menor tamanho. Com um objeto do tipo MemoryStream, recuperamos o conteúdo que é gerado pelo GZipStream e assim salvamos/criamos o arquivo com extensão “*.gz”, já comprimido.

O Método WriteFile tem apenas a finalidade de gerar os arquivos fisicamente no disco, dado um nome do arquivo e o conteúdo que será salvo para este. Abaixo o código deste método para análise:

1
2
3
4
5
6
7
8
 
Public Sub WriteFile(ByVal fileName As String, _
    ByVal data() As Byte, _
    ByVal count As Integer)
 
    Dim f As New FileStream(fileName, FileMode.Create, FileAccess.Write)
    f.Write(data, 0, count)
    f.Close()
End Sub
 
Código 3 – Método auxiliar que gera os arquivos fisicamente.

Agora já analisando a outra parte do código 2, ou melhor, o método “Descomprime”, ele faz basicamente da mesma forma que anteriormente, ou seja, carrega o arquivo comprimido para um FileStream e depois passa a instância de um MemoryStream para a classe fazer o trabalho de descompressão do arquivo informado. Um método auxiliar, chamado “ReadBytes”, é criado para recuperarmos o conteúdo extraído/original do arquivo através do GZipStream.

Depois de executar o código, o arquivo EXE de 527 KB, diminuiu para 225 KB, e com certeza é bem considerável.

Utilizando a Compreensão no ASP.NET

Mas claro que a única utilidade deste namespace não é somente para comprimir e descomprimir arquivos em disco. No ASP.NET, ele terá um papel bastante importante, ou seja, poderemos através dele, comprimirmos o conteúdo/output de páginas ASPX antes de enviar ao cliente (clientes que suportam este tipo de compressão), para termos uma melhor performance e consequentemente diminuimos o tráfego de dados na rede.

Para aqueles que utilizam IIS 6.0, pode ir até as propriedades da aplicação, na Tab “Service” e marcar a compressão de arquivos, já que a mesma, por padrão, vem desabilitada e assim não seria necessário nenhuma espécie de programação, pois o IIS cuidaria disso.

Mas há casos em que não temos o IIS 6.0 ou o acesso a ele. Isso ocorre quando temos a nossa aplicação hospedada em um host qualquer, e em quase 100% dos casos, o cliente não tem acesso ao IIS. Nesta situação é que o ASP.NET 2.0 + .NET Framework 2.0 nos ajudam, pois podemos comprimir os “arquivos” programaticamente através de HttpModules e fazendo também o uso das classes de compressão que vimos no início do artigo.

Chamo a atenção aqui que é extremamente necessário que o cliente tenha habilitado no seu browser o suporte ao HTTP 1.1, pois a compressão é uma “feature” disponibilizada nesta versão. Caso ela não esteja configurada, então o processo não funcionará. Inclusive você poderá identificar isso programaticamente no próprio ASP.NET, ou mesmo, através do Trace.

Para habilitar o suporte ao HTTP 1.1 no seu browser (Internet Explorer), que por padrão já vem habilitado, deve-se ir até o menu Tools (Ferramentas), Internet Options (Opções da Internet), tab Advanced (Avançado), e habilitar a opção “Use HTTP 1.1”.

Para o nosso teste, infelizmente não tenho no momento ferramentas de Monitoring (como por exemplo a Network Monitor Tools, disponibilizada no Windows 2003 server), justamente por estar desenvolvendo este artigo em Windows XP Professional. Apenas para exemplo, vamos verificar através do Trace da página qual dos tipos de compressão foi usado. Vale lembrar que isso não diminui o tamanho final do output da página, ou seja, a finalidade é que o servidor compacta o retorno e envia ao cliente, e este, por sua vez, recebe este conteúdo e automaticamente descompacta e exibe ao usuário e por esta questão é que temos que ter o suporte ao HTTP 1.1.

O projeto constituirá em um Class Library que criaremos uma classe que implementará a interface IHttpModule. Esse processo é bem semelhante o que já fazemos atualmente, ou seja, no evento Init anexamos o procedimento que será disparado quando o evento BeginRequest for invocado. A diferença está na codificação deste procedimento, onde será nele que verificaremos se a requisição que solicitou a página suporta a compressão e assim compactamos o retorno de acordo com os algoritmos suportados pelos browser. Veremos abaixo o componente já implementado:

1
2
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 
Imports Microsoft.VisualBasic
Imports System.IO
Imports System.IO.Compression
Imports System.Web
 
Public Class Compress
 
    Implements IHttpModule
 
    Public Sub Dispose() Implements System.Web.IHttpModule.Dispose
    End Sub
 
    Public Sub Init(ByVal context As HttpApplication) Implements System.Web.IHttpModule.Init
        AddHandler context.BeginRequest, AddressOf BeginRequest
    End Sub
 
    Private Sub BeginRequest(ByVal sender As Object, ByVal e As EventArgs)
        Dim app As HttpApplication = DirectCast(sender, HttpApplication)
        Dim headers As String = app.Request.Headers.Get(“Accept-Encoding”)
        If Not headers = String.Empty Then
            Dim stream As Stream = app.Response.Filter
            If headers.ToUpper.IndexOf(“GZIP”) > -1 Then
                app.Response.Filter = New GZipStream(stream, CompressionMode.Compress)
                app.Response.AppendHeader(“Content-Encoding”, “gzip”)
                app.Context.Trace.Warn(“Compressao via GZIP”)
            ElseIf headers.ToUpper.IndexOf(“DEFLATE”) > -1 Then
                app.Response.Filter = New DeflateStream(stream, CompressionMode.Compress)
                app.Response.AppendHeader(“Content-Encoding”, “deflate”)
                app.Context.Trace.Warn(“Compressao via DeflateStream”)
            End If
        End If
    End Sub
 
End Class
 
Código 4 – Implementando a interface IHttpModule para compreesão.

Analisando o código acima, no procedimento BeginRequest recuperamos a instância do objeto HttpApplication e com este, fazemos as devidas consistências, recuperamos e definimos o tipo de retorno. Através do método Get que é fornecida pela propriedade Headers (que contém o cabeçalho da requisição HTTP), recuperamos o Accept-Encoding, que nos informará se o cliente tem ou não suporte aos tipos de compactação do .NET (GZipStream e DeflateStream).

Baseado neste valor, criamos a instância do “compactador” que será utilizado, e com isso, atribuímos ao construtor da classe responsável pela compactação o Stream que é devolvido para o cliente, justamente para internamente, ela criar a versão compactada desta página e devolver para o cliente.

Nesta altura o componente já está criado e com a DLL gerada. Temos agora que adicionar a referência ao nosso projeto ASP.NET Application. Depois de feita a referência, temos que, no arquivo Web.Config desta aplicação ASP.NET, adicionar o HttpModule para que ele possa ser executado nas requisições das páginas.

No exemplo deste artigo, o projeto Class Library foi chamado de “Component” e a classe responsável pela compressão de “Compress”. Então adicionamos este módulo no arquivo Web.Config da aplicação, assim como já fazíamos anteriormente, ou seja, dentro da seção “<system.web></system.web>”, como é mostrado abaixo:

1
2
3
 
<httpModules>
    <add name=”Compress” type=”Component.Compress, Component”/>
</httpModules>
 
Código 5 – Configurando o Módulo no arquivo Web.Config.

Depois disso, ao rodar a aplicação a compactação passa a ser realizada pelo ASP.NET, assim como podemos ver na Figura 1 logo abaixo onde o Trace nos aponta qual dos “compactadores” foi utilizado.

Figura 1 – Verificando qual compactador foi utilizado.

CONCLUSÃO: Através destas classes agora disponíveis nesta nova versão do .NET Framework, podemos criar diversas funcionalidades para as nossas aplicações, sem a necessidade de adquirir um componente de terceiros. Este novo namespace, System.IO.Compression fornece todo o suporte necessário para atingir os objetivos quando se trata de compressão e descompressão de arquivos em .NET.

Anúncios

2 comentários sobre “Compressão de Arquivos

  1. Israel, primeiramente, meus parabéns pelo tutorial!

    Eu usei e deu certo, mas com um problema:
    O arquivo compactado dentro do arquivo ZIP fica sem extensão, você saberia me dizer porque isso acontece?

    Obrigado,
    Giovani

  2. Boas Giovani,

    Com GZipStream, basta você salvar o nome do arquivo da seguinte forma (de acordo com o arquivo que está compactando):

    Dados.txt.zip
    Dados.doc.zip

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s