Python: Inflar y desinflar implementaciones


Estoy interactuando con un servidor que requiere que los datos enviados a él se comprimen con el algoritmo Deflate (codificación Huffman + LZ77) y también envía datos que necesito Inflar.

Sé que Python incluye Zlib, y que las bibliotecas C en Zlib admiten llamadas a Inflate y Deflate, pero aparentemente estas no son proporcionadas por el módulo Python Zlib. Proporciona Comprimir y Descomprimir, pero cuando hago una llamada, tales como lo siguiente:

result_data = zlib.decompress( base64_decoded_compressed_string )

Recibo el siguiente error:

Error -3 while decompressing data: incorrect header check

Gzip no lo hace mejor; al hacer una llamada como:

result_data = gzip.GzipFile( fileobj = StringIO.StringIO( base64_decoded_compressed_string ) ).read()

Recibo el error:

IOError: Not a gzipped file

Lo que tiene sentido ya que los datos son un archivo Deflatedno un verdadero archivo Gzipped.

Ahora sé que hay una implementación Deflate disponible (Pyflate), pero no sé de una implementación Inflate.

Parece que hay algunos opciones:

  1. Encuentra una implementación existente (ideal) de Inflate y Deflate en Python
  2. Escriba mi propia extensión Python en la biblioteca zlib c que incluye Inflate y Deflate
  3. Llama a otra cosa que se puede ejecutar desde la línea de comandos (como un script Ruby, ya que Infla/Deflate las llamadas en zlib están completamente envueltas en Ruby)
  4. ?

Estoy buscando una solución, pero al carecer de una solución, estaré agradecido por las ideas, opiniones constructivas e ideas.

Información Adicional: El resultado de desinflar (y codificar) una cadena debería, para los fines que necesito, dar el mismo resultado que el siguiente fragmento de código C#, donde el parámetro de entrada es una matriz de bytes UTF correspondientes a los datos a comprimir:

public static string DeflateAndEncodeBase64(byte[] data)
{
    if (null == data || data.Length < 1) return null;
    string compressedBase64 = "";

    //write into a new memory stream wrapped by a deflate stream
    using (MemoryStream ms = new MemoryStream())
    {
        using (DeflateStream deflateStream = new DeflateStream(ms, CompressionMode.Compress, true))
        {
            //write byte buffer into memorystream
            deflateStream.Write(data, 0, data.Length);
            deflateStream.Close();

            //rewind memory stream and write to base 64 string
            byte[] compressedBytes = new byte[ms.Length];
            ms.Seek(0, SeekOrigin.Begin);
            ms.Read(compressedBytes, 0, (int)ms.Length);
            compressedBase64 = Convert.ToBase64String(compressedBytes);
        }
    }
    return compressedBase64;
}

Ejecutar este código. NET para la cadena "deflate and encode me" da el resultado

7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8iZvl5mbV5mi1nab6cVrM8XeT/Dw==

Cuando " desinflar y encode me " se ejecuta a través de Python Zlib.compress () y luego codificado base64, el resultado es "eJxLSU3LSSxJVUjMS1FIzUvOT0lVyE0FAFXHB6k=".

Está claro que zlib.compress () no es una implementación del mismo algoritmo que el algoritmo Deflate estándar.

Más Información:

Los primeros 2 bytes de los datos de deflación.NET ("7b0HY..."), después de la decodificación b64 son 0xEDBD, que no corresponde a datos Gzip (0x1f8b), datos BZip2 (0x425A), o Zlib (0x789C) datos.

Los primeros 2 bytes de los datos comprimidos de Python ("eJxLS..."), después de la decodificación b64 son 0x789C. Este es un encabezado Zlib.

RESUELTO

Para manejar el desinflar e inflar raw, sin encabezado y suma de verificación, las siguientes cosas tenían que suceder:

Al desinflar/comprimir: elimine los dos primeros bytes (encabezado) y los últimos cuatro bytes (suma de comprobación).

Al inflar/descomprimir: hay un segundo argumento para el tamaño de la ventana. Si este valor es negativo suprime los encabezados. aquí están mis métodos actualmente, incluyendo la codificación/decodificación base64-y funcionando correctamente:

import zlib
import base64

def decode_base64_and_inflate( b64string ):
    decoded_data = base64.b64decode( b64string )
    return zlib.decompress( decoded_data , -15)

def deflate_and_base64_encode( string_val ):
    zlibbed_str = zlib.compress( string_val )
    compressed_string = zlibbed_str[2:-4]
    return base64.b64encode( compressed_string )
Author: msanford, 2009-07-07

2 answers

Este es un complemento a la respuesta de MizardX, dando alguna explicación y antecedentes.

Véase http://www.chiramattel.com/george/blog/2007/09/09/deflatestream-block-length-does-not-match.html

De acuerdo con RFC 1950 , un flujo zlib construido de la manera predeterminada se compone de:

  • un encabezado de 2 bytes (por ejemplo, 0x78 0x9C)
  • una corriente de deflación see ver RFC 1951
  • una suma de comprobación Adler-32 de los datos sin comprimir (4 bytes)

El C# DeflateStream funciona en (lo has adivinado) un flujo de deflate. El código de MizardX le dice al módulo zlib que los datos son un flujo de desinflado sin procesar.

Observaciones: (1) Se espera que el método de "deflación" de C# que produce una cadena más larga solo ocurra con una entrada corta (2) Utilizando el flujo de deflación sin la suma de verificación Adler-32? Un poco arriesgado, a menos que sea reemplazado por algo mejor.

Actualizaciones

Mensaje de Error Block length does not match with its complement

Si están tratando de inflar algunos datos comprimidos con el C # DeflateStream y se obtiene ese mensaje, entonces es muy posible que usted está dando un flujo zlib, no un flujo de desinflar.

Ver ¿Cómo se utiliza un DeflateStream en parte de un archivo?

También copie/pegue el mensaje de error en una búsqueda de Google y obtendrá numerosos resultados (incluido el que está al frente de esta respuesta) diciendo casi lo mismo.

El Java Deflater ... utilizado por el " sitio" ... C# DeflateStream "es bastante sencillo y ha sido probado contra la implementación de Java". ¿Cuál de los siguientes posibles constructores de Deflater de Java está utilizando el sitio web?

public Deflater(int level, boolean nowrap)

Crea un nuevo compresor utilizando el nivel de compresión especificado. Si' nowrap ' es true, entonces el encabezado ZLIB y los campos checksum no se usarán para soportar el formato de compresión usado tanto en GZIP como en PKZIP.

public Deflater(int level)

Crea un nuevo compresor utilizando el nivel de compresión especificado. Los datos comprimidos se generarán en formato ZLIB.

public Deflater()

Crea un nuevo compresor con el nivel de compresión predeterminado. Los datos comprimidos se generarán en formato ZLIB.

Un deflater de una línea después de tirar el encabezado zlib de 2 bytes y la suma de verificación de 4 bytes:

uncompressed_string.encode('zlib')[2:-4] # does not work in Python 3.x

O

zlib.compress(uncompressed_string)[2:-4]
 19
Author: John Machin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:45:22

Todavía se puede utilizar el zlib módulo para inflar / desinflar datos. Las gzip module lo usa internamente, pero agrega un encabezado de archivo para convertirlo en un archivo gzip. Mirando a la gzip.py archivo, algo como esto podría funcionar:

import zlib

def deflate(data, compresslevel=9):
    compress = zlib.compressobj(
            compresslevel,        # level: 0-9
            zlib.DEFLATED,        # method: must be DEFLATED
            -zlib.MAX_WBITS,      # window size in bits:
                                  #   -15..-8: negate, suppress header
                                  #   8..15: normal
                                  #   16..30: subtract 16, gzip header
            zlib.DEF_MEM_LEVEL,   # mem level: 1..8/9
            0                     # strategy:
                                  #   0 = Z_DEFAULT_STRATEGY
                                  #   1 = Z_FILTERED
                                  #   2 = Z_HUFFMAN_ONLY
                                  #   3 = Z_RLE
                                  #   4 = Z_FIXED
    )
    deflated = compress.compress(data)
    deflated += compress.flush()
    return deflated

def inflate(data):
    decompress = zlib.decompressobj(
            -zlib.MAX_WBITS  # see above
    )
    inflated = decompress.decompress(data)
    inflated += decompress.flush()
    return inflated

No se si esto corresponde exactamente a lo que su servidor requiera, pero esas dos funciones son capaces de dar la vuelta a cualquier dato que probé.

Los parámetros se asignan directamente a lo que se pasa a la biblioteca zlib función.

PythonC
zlib.compressobj(...)deflateInit(...)
compressobj.compress(...)deflate(...)
zlib.decompressobj(...)inflateInit(...)
decompressobj.decompress(...)inflate(...)

Los constructores crean la estructura y la rellenan con valores predeterminados, y la pasan a las funciones init. Las compress/decompress métodos actualizar la estructura y pasarla a inflate/deflate.

 19
Author: Markus Jarderot,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-07-08 21:18:16