Python: gonfler et dégonfler les implémentations

je suis en interface avec un serveur qui exige que les données qui lui sont envoyées soient compressées avec Deflate algorithme (Huffman encoding + LZ77) et envoie également des données dont j'ai besoin pour gonfler .

je sais que Python inclut Zlib, et que les bibliothèques C dans Zlib supportent les appels à Inflate et Deflate , mais ceux-ci ne sont apparemment pas fournis par le module Python Zlib. Il ne fournir Compresser et Décompresser , mais quand je fais un appel tel que le suivant:

result_data = zlib.decompress( base64_decoded_compressed_string )

je reçois l'erreur suivante:

Error -3 while decompressing data: incorrect header check

Gzip n'est pas mieux; lors d'un appel tel que:

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

je reçois l'erreur:

IOError: Not a gzipped file

ce qui a du sens puisque les données sont un fichier DEFLATE pas un vrai Gzippé "151990920 de fichier".

Maintenant, je sais qu'il y a un Dégonfler la mise en œuvre disponible (Pyflate), mais je ne sais pas d'un Gonfler la mise en œuvre.

il semble qu'il y ait quelques options:

  1. trouver une implémentation existante (idéale) de Inflate et Deflate en Python
  2. écrire ma propre extension Python à la bibliothèque zlib c qui inclut Inflate et Deflate
  3. appeler autre chose qui peut être exécuté à partir de la ligne de commande (comme un script Ruby, depuis Inflate / Deflate les appels en zlib sont entièrement enveloppés dans Ruby)
  4. ?

je cherche un solution, mais sans que une solution, je vous en sera reconnaissant, idées, opinions constructives, et des idées.

informations supplémentaires : Le résultat de la déflation (et l'encodage d'une chaîne doit, pour l'application dont j'ai besoin, donne le même résultat que l'extrait suivant du code C#, où le paramètre d'entrée est un tableau d'octets UTF correspondant aux données à compresser:

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

exécutant ce code .NET pour la chaîne "deflate et encodez moi "donne le résultat

7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8iZvl5mbV5mi1nab6cVrM8XeT/Dw==

quand "deflate and encode me" est exécuté à travers le zlib de Python.compress () puis base64 encodé, le résultat est " eJxLSU3LSSxJVUjMS1FIzUvOT0lVyE0Fafxhb6k=".

il est clair que zlib.compress() n'est pas une implémentation du même algorithme que L'algorithme Deflate standard.

Plus D'Information :

les 2 premiers octets de les données déflatées du réseau ("7b0HY..."), après le décodage b64 sont 0xEDBD, qui ne correspond pas aux données Gzip (0x1f8b), bzip2 (0x425A) ou Zlib (0x789C).

les 2 premiers octets des données compressées Python ("eJxLS..."), après B64 décodage sont 0x789C. C'est un en-tête Zlib.

résolu

pour gérer le dégonflé brut et gonfler, sans en-tête et checksum, les choses suivantes nécessaires pour arriver:

Sur dégonfler/compress: bande les deux premiers octets (en-tête) et les quatre derniers octets (somme de contrôle).

sur gonfler/décompresser: il y a un deuxième argument pour la taille de la fenêtre. Si cette valeur est négative, elle supprime les en-têtes. voici mes méthodes actuellement, y compris l'encodage/décodage base64-et fonctionne correctement:

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 )
45
demandé sur msanford 2009-07-07 03:24:10

2 réponses

il s'agit d'un ajout à la réponse de MizardX, donnant quelques explications et arrière-plan.

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

Selon RFC 1950 , un zlib flux construits par défaut dans la manière est composé de:

  • 2 octets de l'en-tête (par exemple, 0x78 0x9C)
  • un dégonfler stream -- voir RFC 1951
  • l'Adler-32 de la somme de contrôle des données non compressées (4 octets)

C# DeflateStream (vous l'aurez deviné) un dégonfler flux. Le code de MizardX indique au module zlib que les données sont un flux deflate brut.

Observations: (1) on espère que la méthode c# "deflation" produisant une chaîne plus longue se produit seulement avec l'entrée courte (2) en utilisant le raw deflate stream sans le checksum Adler-32? Un peu risqué, à moins d'être remplacé par quelque chose de mieux.

Mises à jour

message d'erreur Block length does not match with its complement

si vous essayez de gonfler des données compressées avec le C# DeflateStream et que vous recevez ce message, alors il est tout à fait possible que vous lui donniez un flux zlib, pas un flux deflate.

voir comment utiliser une Déflatestream sur une partie d'un fichier?

également copier/coller le message d'erreur dans une recherche Google et vous obtiendrez de nombreux résultats (y compris celui au début de cette réponse) disant à peu près la même chose.

The Java Deflater ... utilisé par "le site" ... C # DeflateStream "est assez simple et a été testé sur Java application." Lequel des constructeurs Java Deflater suivants utilise le site web?

public Deflater(int level, boolean nowrap)

crée un nouveau compresseur en utilisant le niveau de compression spécifié. Si 'nowrap' est vrai, alors les champs d'en-tête ZLIB et checksum ne seront pas utilisés pour supporter le format de compression utilisé dans GZIP et PKZIP.

public Deflater(int level)

crée un nouveau compresseur utilisant le niveau de compression spécifié. Les données compressées seront générées au format ZLIB.

public Deflater()

crée un nouveau compresseur avec le niveau de compression par défaut. Les données compressées seront générées au format ZLIB.

Une ligne de deflater après de jeter le 2 octets zlib et l'en-tête 4 octets somme de contrôle:

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

ou

zlib.compress(uncompressed_string)[2:-4]
19
répondu John Machin 2017-05-23 11:45:22

vous pouvez toujours utiliser le module zlib pour gonfler/déflater les données. Le module gzip l'utilise en interne, mais ajoute un en-tête de fichier pour en faire un gzip-file. En regardant le gzip.py fichier, quelque chose comme cela pourrait fonctionner:

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

Je ne sais pas si cela correspond exactement à ce dont votre serveur a besoin, mais ces deux fonctions sont capables d'aller-retour toutes les données que j'ai essayé.

les paramètres correspondent directement à ce qui est passé aux fonctions de la bibliothèque zlib.

Python C

zlib.compressobj(...)deflateInit(...)

compressobj.compress(...)deflate(...)

zlib.decompressobj(...)inflateInit(...)

decompressobj.decompress(...)inflate(...)

les constructeurs créez la structure et peuplez-la avec des valeurs par défaut, et passez-la aux fonctions d'initialisation. Les méthodes compress / decompress mettent à jour la structure et la passent à inflate / deflate .

19
répondu Markus Jarderot 2014-07-08 21:18:16