2009-07-06 16 views
40

sto interfacciamento con un server che richiede che i dati inviati ad esso è compresso con Deflate algoritmo (codifica di Huffman + LZ77) e invia i dati che ho bisogno di Gonfiare.Python: gonfiare e sgonfiare le implementazioni

So che Python include Zlib, e che le librerie C di chiamate di assistenza Zlib a Gonfiare e Sgonfiare, ma questi a quanto pare non sono forniti dal modulo Python zlib. Esso fornisce Comprimere e decomprimere, ma quando faccio una chiamata come la seguente:

result_data = zlib.decompress(base64_decoded_compressed_string) 

ricevo il seguente errore:

Error -3 while decompressing data: incorrect header check 

Gzip non fa meglio; quando si effettua una chiamata come ad esempio:

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

ricevo l'errore:

IOError: Not a gzipped file 

che ha un senso in quanto i dati è un Sgonfiato non file di un vero e proprio Gzipped file.

Ora so che c'è un Sgonfiare implementazione disponibili (Pyflate), ma non so di un implementazione Inflate.

Sembra che ci sono alcune opzioni:

  1. Trova un'implementazione esistente (ideale) di Gonfiate e Sgonfiare in Python
  2. scrivere il mio estensione Python al zlib c libreria che include Gonfia e Deflate
  3. Chiama qualcos'altro che può essere eseguito dalla riga di comando (ad esempio uno script rubino, poiché gonfiare/Deflate chiamate in zlib sono completamente avvolti in rubino)
  4. ?

Sto cercando una soluzione, ma senza una soluzione sarò grato per intuizioni, opinioni costruttive e idee.

Ulteriori informazioni: Il risultato di sgonfiamento (e codifica) una stringa dovrebbe, ai fini ho bisogno, dare lo stesso risultato come il seguente frammento di codice C#, in cui il parametro di ingresso è un array di byte UTF corrispondente ai dati da comprimere:

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; 
} 

Esecuzione di questo.NET codice per la stringa "sgonfiare e mi codificare" dà il risultato

7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8iZvl5mbV5mi1nab6cVrM8XeT/Dw== 

Quando "sgonfiare e mi codificare" è gestito attraverso il Python Zlib.compress() e poi codificato Base64, il risultato è "eJxLSU3LSSxJVUjMS1FIzUvOT0lVyE0FAFXHB6k =" .

È chiaro che zlib.compress() non è un'implementazione dello stesso algoritmo dell'algoritmo Deflate standard.

Ulteriori informazioni:

I primi 2 byte del NET sgonfiano dati ("7b0HY ..."), dopo la decodifica b64 sono 0xEDBD, che non corrisponde ai dati Gzip (0x1f8b), BZip2 (0x425A) dati o dati Zlib (0x789C).

I primi 2 byte dei dati compressi Python ("eJxLS ..."), dopo la decodifica b64 sono 0x789C. Questa è un'intestazione Zlib.

risolto

Per gestire la sgonfiare grezzo e gonfiare, senza intestazione e checksum, le seguenti cose necessarie accadano:

On deflate/compressa: nudo i primi due byte (intestazione) e la ultimi quattro byte (checksum).

Su gonfia/decompressione: esiste un secondo argomento per la dimensione della finestra. Se questo valore è negativo, sopprime le intestazioni. qui sono i miei metodi attualmente, tra cui la codifica Base64/decodifica - e funziona correttamente:

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) 

risposta

16

Questo è un componente aggiuntivo della risposta di MizardX, che fornisce alcune spiegazioni e informazioni.

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

Secondo RFC 1950, un flusso zlib costruito nel modo predefinito è composto di:

  • un'intestazione 2-byte (ad esempio 0x78 0x9C)
  • un flusso sgonfiare - vedi RFC 1951
  • un checksum Adler-32 dei dati non compressi (4 byte)

Il C# DeflateStream funziona su (avete indovinato) un flusso di deflate. Il codice di MizardX sta dicendo al modulo zlib che i dati sono un flusso grezzo di deflate.

Osservazioni: (1) Si spera che il metodo di "sgonfiaggio" del C# producendo una stringa più lunga avvenga solo con un input breve (2) Usando il flusso grezzo di deflate senza il checksum Adler-32? Un po 'rischioso, a meno che non venga sostituito con qualcosa di meglio.

Aggiornamenti

messaggio di errore Block length does not match with its complement

Se si sta tentando di gonfiare alcuni dati compressi con il C# DeflateStream e si ottiene quel messaggio, allora è molto probabile che si sta dando a bis flusso zlib, non un flusso di deflate.

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

copiare anche/incolla il messaggio di errore in una ricerca su Google e si ottengono numerosi successi (tra cui quella la parte anteriore di questa risposta), dicendo più o meno la stessa cosa.

Java Deflater ... utilizzato dal "sito" ... C# DeflateStream "è abbastanza semplice ed è stato testato contro l'implementazione Java". Quale dei seguenti costruttori Java Deflater è possibile utilizzare il sito 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.

A una riga deflater dopo buttare via l'intestazione zlib 2 byte e il checksum a 4 byte:

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

o

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

+1 Grazie per le informazioni aggiuntive. – Demi

+0

@John Machin: Per rispondere alla tua prima osservazione ... il risultato è solo più lungo nel caso di stringhe più corte (intestazione? Padding?). Quando inserisco 161 byte di dati per la deflazione, prima della codifica base64 il risultato è 126 byte. – Demi

+0

@John Machin: grandi approfondimenti e informazioni. La firma Java del deflater utilizzato è quella con due parametri, con nowrap == true. Ho usato il tuo esempio di deflattore a una riga e si gonfia bene in .NET e Java, nonostante sia diverso dal valore prodotto deflazionandosi con le librerie in quelle lingue. È grandioso Ora sto lavorando per gonfiare - prendendo i dati sgonfiati prodotti da Java o .NET e aggiungendo un checksum adler32 e l'intestazione zlib per vedere se riesco a far sì che Python lo consumi bene. Ti farò sapere come va. – Demi

17

È comunque possibile utilizzare il modulo zlib di gonfiare/sgonfiare i dati. Il modulo gzip lo utilizza internamente, ma aggiunge un'intestazione di file per trasformarlo in un file gzip. Guardando il file gzip.py, qualcosa di simile potrebbe funzionare:

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 

Non so se questo corrisponde esattamente a ciò che il server richiede, ma queste due funzioni sono in grado di andata e ritorno tutti i dati che ho provato.

I parametri vengono mappati direttamente a ciò che viene passato alle funzioni della libreria zlib.

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

I costruttori creano la struttura e la popolano con valori predefiniti e la passano alle funzioni init. I metodi compress/decompress aggiornano la struttura e la passano a inflate/deflate.

+0

Quello che sto cercando poiché è l'accesso al livello C Gonfia e sgonfia le chiamate della libreria che il modulo Python Zlib esegue. Non sembra che Decompress e Compress facciano la stessa cosa, e il modulo Python Zlib non espone la funzione Inflate e Deflate – Demi

+0

Questo non è utile. Si prega di notare le informazioni aggiuntive che ho aggiunto alla mia domanda di cui sopra. Il codice che hai fornito sopra, quando viene eseguito con la stringa "deflate and encode me", restituisce "S0lNy0ksSVVIzEtRSM1Lzk9JVchNBQA =", che è ancora più breve. Il risultato corretto di Deflate dovrebbe apparire come la stringa (più lunga) generata da .NET che ho notato sopra. – Demi

+0

In che modo una stringa di input di 21 caratteri genera un'uscita deflazionata di 212 byte? Questo include un'intestazione del file deflate? –

Problemi correlati