2009-07-06 27 views
40

estoy de interfaz con un servidor que requiere que los datos enviados a ella se comprime con Deflate algoritmo (codificación Huffman + LZ77) y también envía los datos que necesito Inflar.Python: inflar y desinflar implementaciones

sé que Python incluye Zlib, y que las bibliotecas de C en las llamadas de soporte Zlib a Inflar y desinflado , pero estos al parecer no son proporcionados por el módulo de Python Zlib. Sí que da Comprimir y Descomprimir , pero cuando hago una llamada tales como los siguientes:

result_data = zlib.decompress(base64_decoded_compressed_string) 

recibo el siguiente error:

Error -3 while decompressing data: incorrect header check 

gzip no es mejor; al realizar una llamada, tales como:

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

recibo el error:

IOError: Not a gzipped file 

lo cual tiene sentido ya que los datos no es un archivo dedesinflado un verdadero archivo comprimido en GZIP .

Ahora sé que hay una desinflado aplicación disponible (Pyflate), pero no sé de una aplicacióninflado.

Parece que hay algunas opciones:

  1. Encuentra una implementación existente (ideal) de Inflar y desinflado en Python
  2. escribir mi propia extensión de Python a la zlib c biblioteca que incluye Inflar y Deflate
  3. Llamar a algo más que pueda ser ejecutado desde la línea de comandos (por ejemplo, un script Ruby, ya Inflar/desinflado llamadas en zlib están completamente envueltos en Rubí)
  4. ?

Estoy buscando una solución, pero al carecer de una solución agradeceré ideas, opiniones constructivas e ideas.

Información adicional: El resultado de desinflar (y codificación) de una cadena debe, a los efectos que necesito, dar el mismo resultado que el siguiente fragmento de código C#, donde el parámetro de entrada es una serie de bytes UTF correspondiente a los datos para 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; 
} 

Ejecutando esto.código NET para la cadena "desinflar y me codificar" da el resultado

7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8iZvl5mbV5mi1nab6cVrM8XeT/Dw== 

Cuando "desinflar y me codificar" se ejecuta a través de la Zlib.compress Python() y luego 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 2 primeros bytes del .NET desinflan los datos ("7b0HY ..."), después de la decodificación b64 son 0xEDBD, que no corresponde a los datos de Gzip (0x1f8b), BZip2 (0x425A) datos, o datos Zlib (0x789C).

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 deflate prima e inflar, sin cabecera y suma de comprobación, las siguientes cosas necesarias para suceden:

En deflate/compresa: tira de los dos primeros bytes (de cabecera) y la 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 base 64 de codificación/decodificación - y funciona 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) 

Respuesta

16

Este es un complemento de la respuesta de MizardX, dando algunas explicaciones y antecedentes.

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

Según RFC 1950, una corriente zlib construido de la manera predeterminada se compone de:

  • una cabecera de 2 bytes (por ejemplo, 0x78 0x9C)
  • una corriente deflate - ver RFC 1951
  • un Adler-32 de suma de comprobación de los datos no comprimidos (4 bytes)

El C# DeflateStream funciona en (lo adivinó) una secuencia desinflada. El código de MizardX le dice al módulo zlib que los datos son una corriente de desinflado sin formato.

Observaciones: (1) Se espera que el método C# "deflación" que produce una cadena más larga ocurra solo con entrada corta (2) ¿Usando la corriente desinflada sin la suma de comprobación Adler-32? Poco arriesgado, a menos que sea reemplazado con algo mejor.

Actualizaciones

mensaje de error Block length does not match with its complement

Si usted está tratando de inflar algunos datos comprimidos con el C# DeflateStream y se obtiene ese mensaje, entonces es muy posible que usted le está dando aa zlib stream, no una secuencia desinflada.

Ver How do you use a DeflateStream on part of a file?

También copiar/pegar el mensaje de error en una búsqueda en Google y obtendrá numerosos hits (incluyendo la que la parte delantera de esta respuesta) diciendo lo mismo.

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

public Deflater(int level, boolean nowrap)

Creates a new compressor using the specified compression level. If 'nowrap' is true then the ZLIB header and checksum fields will not be used in order to support the compression format used in both GZIP and PKZIP.

public Deflater(int level)

Creates a new compressor using the specified compression level. Compressed data will be generated in ZLIB format.

public Deflater()

Creates a new compressor with the default compression level. Compressed data will be generated in ZLIB format.

Un deflactor de una línea después de haber tirado la cabecera zlib de 2 bytes y la suma de comprobación de 4 bytes:

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

o

zlib.compress(uncompressed_string)[2:-4] 
+0

+1 Gracias por la información adicional. – Demi

+0

@John Machin: Para responder a su primera observación ... el resultado es solo más largo en el caso de cadenas más cortas (¿encabezado? ¿Relleno?). Cuando introduzco 161 bytes de datos para deflación, antes de la codificación base64 el resultado es 126 bytes. – Demi

+0

@John Machin: excelentes ideas e información. La firma Java de deflater utilizada es la que tiene dos parámetros, con nowrap == verdadero. Usé su ejemplo de deflactor de una línea y se infla bien en .NET y Java, a pesar de tener un aspecto diferente al valor producido al desinflarse con las bibliotecas en esos idiomas. Esto es genial. Ahora estoy trabajando en inflar - tomando los datos desinflados producidos por Java o .NET y agregando una suma de comprobación adler32 y el encabezado zlib para ver si puedo lograr que Python lo consuma bien. Te dejaré saber cómo va. – Demi

17

Puede seguir utilizando el módulo zlib para inflar/desinflar datos. El módulo gzip lo usa internamente, pero agrega un encabezado de archivo para convertirlo en un archivo gzip. Mirando el archivo gzip.py, 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 sé si esto corresponde exactamente a lo que su servidor requiere, pero esas dos funciones son capaces de ida y vuelta los datos que lo intenté.

Los parámetros se correlacionan directamente con lo que se pasa a las funciones de la biblioteca zlib.

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

Los constructores crean la estructura y la completan con valores predeterminados, y la transfieren a las funciones de inicio. Los métodos compress/decompress actualizan la estructura y la pasan a inflate/deflate.

+0

Lo que estoy buscando for es el acceso a las llamadas Inflate y Deflate del nivel C de la biblioteca que envuelve el módulo Python Zlib. No parece que Descomprimir y Comprimir hagan lo mismo, y el módulo Python Zlib no expone Inflar y desinflar – Demi

+0

Esto no es útil. Tenga en cuenta la información adicional que agregué a mi pregunta anterior. El código que proporcionó anteriormente, cuando se ejecuta con la cadena "desinflar y codificarme", da como resultado "S0lNy0ksSVVIzEtRSM1Lzk9JVchNBQA =", que es aún más corto. El resultado correcto de desinflar debería verse como la cadena (más larga) .NET generada que anoto arriba. – Demi

+0

¿Cómo resulta una cadena de entrada de 21 caracteres en una salida desinflada de 212 bytes? ¿Eso incluye un encabezado de archivo desinflado? –

Cuestiones relacionadas