Como posso descompactar um fluxo gzip com zlib?

108

Os arquivos de formato Gzip (criados com o gzipprograma, por exemplo) usam o algoritmo de compressão "deflate", que é o mesmo algoritmo de compressão que o zlib usa. No entanto, ao usar zlib para aumentar um arquivo compactado gzip, a biblioteca retorna um Z_DATA_ERROR.

Como posso usar o zlib para descompactar um arquivo gzip?

Greg Hewgill
fonte

Respostas:

118

Para descompactar um arquivo no formato gzip com zlib, chame inflateInit2com o windowBitsparâmetro as 16+MAX_WBITS, assim:

inflateInit2(&stream, 16+MAX_WBITS);

Se você não fizer isso, o zlib reclamará de um formato de stream incorreto. Por padrão, o zlib cria fluxos com um cabeçalho zlib e, ao inflar, não reconhece o cabeçalho gzip diferente, a menos que você diga isso. Embora isso seja documentado a partir da versão 1.2.1 do zlib.harquivo de cabeçalho, não está no manual zlib . Do arquivo de cabeçalho:

windowBitstambém pode ser maior que 15 para decodificação gzip opcional. Adicione 32 a windowBitspara habilitar a decodificação zlib e gzip com detecção automática de cabeçalho ou adicione 16 para decodificar apenas o formato gzip (o formato zlib retornará a Z_DATA_ERROR). Se um fluxo gzip está sendo decodificado, strm->adleré um crc32 em vez de um adler32.

Greg Hewgill
fonte
35
Em python:zlib.decompress(data, 15 + 32)
Roman Starkov
3
Obrigado, isso era muito frustrante até encontrar este post.
Alex
Nossa, essa é a pergunta de 2009. Obrigado @Greg Hewgill
YuAn Shaolin Maculelê Lai
Talvez você possa fornecer algumas diretrizes para a descompressão iterativa do fluxo gzip. Em uma descompressão gzip única, em que o fluxo de saída e o tamanho devem ser fixos e suficientes para armazenar toda a saída descompactada. Este valor depende da eficácia da descompressão gzip, que pode variar de acordo com a entropia dos dados. Existe uma maneira de alocar dinamicamente mais espaço para o buffer de saída quando necessário? Obrigado
Zohar81
103

Pitão

zlibsuporte de biblioteca :

O zlibmódulo python também oferece suporte a eles.

escolhendo windowBits

Mas zlibpode descompactar todos esses formatos:

  • para (des) compactar o deflateformato, usewbits = -zlib.MAX_WBITS
  • para (des) compactar o zlibformato, usewbits = zlib.MAX_WBITS
  • para (des) compactar o gzipformato, usewbits = zlib.MAX_WBITS | 16

Consulte a documentação em http://www.zlib.net/manual.html#Advanced (seção inflateInit2)

exemplos

dados de teste:

>>> deflate_compress = zlib.compressobj(9, zlib.DEFLATED, -zlib.MAX_WBITS)
>>> zlib_compress = zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS)
>>> gzip_compress = zlib.compressobj(9, zlib.DEFLATED, zlib.MAX_WBITS | 16)
>>> 
>>> text = '''test'''
>>> deflate_data = deflate_compress.compress(text) + deflate_compress.flush()
>>> zlib_data = zlib_compress.compress(text) + zlib_compress.flush()
>>> gzip_data = gzip_compress.compress(text) + gzip_compress.flush()
>>> 

teste óbvio para zlib:

>>> zlib.decompress(zlib_data)
'test'

teste para deflate:

>>> zlib.decompress(deflate_data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
zlib.error: Error -3 while decompressing data: incorrect header check
>>> zlib.decompress(deflate_data, -zlib.MAX_WBITS)
'test'

teste para gzip:

>>> zlib.decompress(gzip_data)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
zlib.error: Error -3 while decompressing data: incorrect header check
>>> zlib.decompress(gzip_data, zlib.MAX_WBITS|16)
'test'

os dados também são compatíveis com o gzipmódulo:

>>> import gzip
>>> import StringIO
>>> fio = StringIO.StringIO(gzip_data)
>>> f = gzip.GzipFile(fileobj=fio)
>>> f.read()
'test'
>>> f.close()

detecção automática de cabeçalho (zlib ou gzip)

adicionar 32a windowBitsirá acionar a detecção de cabeçalho

>>> zlib.decompress(gzip_data, zlib.MAX_WBITS|32)
'test'
>>> zlib.decompress(zlib_data, zlib.MAX_WBITS|32)
'test'

usando ao gzipinvés

Para gzipdados com cabeçalho gzip, você pode usar o gzipmódulo diretamente; mas lembre-se de que sob o capô , gzipusa zlib.

fh = gzip.open('abc.gz', 'rb')
cdata = fh.read()
fh.close()
dnozay
fonte
3
por que essa peça de ouro não está nos documentos exatamente neste formato?
Ramon Moraes
sinta-se à vontade para enviar uma solicitação / patch de pull contra cpython usando qualquer uma dessas respostas.
dnozay
ótima resposta para strings, alguma ideia de como fazer isso para um stream sem ler o arquivo inteiro na memória?
Josh J
Obrigado. Posso resolver meu problema de descompactação no meu código-fonte com sua resposta.
Bethlee
incrível, esta é uma pepita de ouro .. no entanto, não posso deixar de sentir que isso é equivalente a 'números mágicos'? onde na documentação isso é mencionado? Eu olhei, mas realmente não devo ter verificado o suficiente .. também, a notação eu não sigo totalmente. O que o | quer dizer, isso é opcional? e porque deflate é negativo .. é MAX_WBITS uma constante .. 🙁
m1nkeh
3

A estrutura do zlib e do gzip é diferente. zlib usa RFC 1950 e gzip usa RFC 1952 , portanto, tem cabeçalhos diferentes, mas o restante tem a mesma estrutura e segue o RFC 1951 .

josep fon
fonte