Подтвердить что ты не робот

Реализации Python: Inflate и Deflate

Я взаимодействую с сервером, который требует, чтобы переданные ему данные были сжаты с помощью алгоритма Deflate (кодировка Хаффмана + LZ77), а также отправляет данные, которые мне нужно надуть.

Я знаю, что Python включает Zlib и что библиотеки C в Zlib поддерживают вызовы Inflate и Deflate, но они, по-видимому, не предусмотрены модулем Python Zlib. Он обеспечивает сжатие и декомпрессию, но когда я звоню, например, следующее:

result_data = zlib.decompress( base64_decoded_compressed_string )

Я получаю следующую ошибку:

Error -3 while decompressing data: incorrect header check

Gzip не лучше; при совершении вызова, например:

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

Я получаю сообщение об ошибке:

IOError: Not a gzipped file

что имеет смысл, поскольку данные являются дефлированным файлом, а не истинным файлом Gzipped.

Теперь я знаю, что есть доступная реализация Deflate (Pyflate), но я не знаю о внедрении Inflate.

Кажется, что есть несколько вариантов:

  • Найти существующую реализацию (идеальную) Inflate и Deflate в Python
  • Напишите мое собственное расширение Python для библиотеки zlib c, которая включает в себя Inflate и Deflate
  • Вызовите что-нибудь еще, которое может быть выполнено из командной строки (например, Ruby script, поскольку вызовы Inflate/Deflate в zlib полностью завернуты в Ruby)

Я ищу решение, но без решения я буду благодарен за идеи, конструктивные мнения и идеи.

Дополнительная информация: Результат дефляции (и кодирования) строки должен для тех целей, которые мне нужны, дать тот же результат, что и следующий фрагмент кода С#, где входным параметром является массив байтов UTF, соответствующий сжатым данным:

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

Запуск этого .NET-кода для строки "deflate and encode me" дает результат

7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8iZvl5mbV5mi1nab6cVrM8XeT/Dw==

Когда "deflate and encode me" запускается через Python Zlib.compress(), а затем закодирован base64, результатом является "eJxLSU3LSSxJVUjMS1FIzUvOT0lVyE0FAFXHB6k =".

Понятно, что zlib.compress() не является реализацией того же алгоритма, что и стандартный алгоритм Deflate.

Дополнительная информация:

Первые 2 байта данных дефляции .NET( "7b0HY..." ) после декодирования b64 являются 0xEDBD, что не соответствует данным Gzip (0x1f8b), данным BZip2 (0x425A) или Zlib (0x789C) данных.

Первые 2 байта сжатых данных Python ( "eJxLS..." ) после декодирования b64 равны 0x789C. Это заголовок Zlib.

решаемые

Чтобы обрабатывать сырые дефлаты и раздувать, без заголовка и контрольной суммы, необходимо следующее:

В режиме deflate/compress: разделите первые два байта (заголовок) и последние четыре байта (контрольная сумма).

При раздувании/распаковке: есть второй аргумент для размера окна. Если это значение отрицательное, оно подавляет заголовки. вот мои методы в настоящее время, в том числе кодирование/декодирование base64 - и работают нормально:

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 )
4b9b3361

Ответ 1

Это дополнение к ответу MizardX, дающее некоторые объяснения и предысторию.

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

Согласно RFC 1950, поток zlib, построенный по умолчанию, состоит из:

  • 2-байтовый заголовок (например, 0x78 0x9C)
  • поток спускания - см. RFC 1951
  • контрольная сумма Adler-32 несжатых данных (4 байта)

С# DeflateStream работает над (вы уже догадались) потоком спускания. Код MizardX сообщает модулю zlib, что данные являются сырым потоком спускания.

Наблюдения: (1) Можно надеяться, что метод С# "дефляция", производящий более длинную строку, происходит только с коротким входом (2) Используя поток сыпучего дефлята без контрольной суммы Adler-32? Бит рискован, если не заменить что-то лучше.

Обновление

сообщение об ошибке Block length does not match with its complement

Если вы пытаетесь раздуть сжатые данные с помощью С# DeflateStream, и вы получите это сообщение, то вполне возможно, что вы даете ему поток zlib, а не поток спускания.

См. Как вы используете DeflateStream для части файла?

Также скопируйте/вставьте сообщение об ошибке в поиск Google, и вы получите многочисленные хиты (в том числе и тот, который стоит перед этим ответом), говорящий примерно то же самое.

Java Deflater... используемый "сайтом"... С# DeflateStream "довольно прост и протестирован против реализации Java". Каким из следующих возможных конструкторов Java Deflater является веб-сайт с использованием?

public Deflater(int level, boolean nowrap)

Создает новый компрессор с использованием указанного уровня сжатия. Если "nowrap" истинно, поля заголовка ZLIB и контрольной суммы не будут использоваться для поддержки формата сжатия, используемого как в GZIP, так и в PKZIP.

public Deflater(int level)

Создает новый компрессор с использованием указанного уровня сжатия. Сжатые данные будут сгенерированы в формате ZLIB.

public Deflater()

Создает новый компрессор с уровнем сжатия по умолчанию. Сжатые данные будут сгенерированы в формате ZLIB.

Однострочный дефлатер после выброса 2-байтового заголовка zlib и 4-байтовой контрольной суммы:

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

или

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

Ответ 2

Вы можете использовать модуль zlib для раздувания/дефляции данных. Модуль gzip использует его внутренне, но добавляет заголовок файла, чтобы превратить его в gzip файл. Глядя на файл gzip.py, что-то вроде этого может работать:

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

Я не знаю, соответствует ли это точно тому, что требуется вашему серверу, но эти две функции могут округлять любые данные, которые я пытался.

Параметры сопоставляются непосредственно с тем, что передается библиотечным функциям zlib.

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

Конструкторы создают структуру и заполняют ее значениями по умолчанию и передают их в init-functions. Методы compress/decompress обновляют структуру и передают ее в inflate/deflate.