Реализации 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 

Гзип не лучше; при звонке, например:

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

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

 IOError: Not a gzipped file 

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

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

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

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

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

Дополнительная информация : Результат дефляции (и кодирования) строки должен для тех целей, которые мне нужны, дать тот же результат, что и следующий фрагмент кода C #, где входным параметром является массив байтов 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 ) 

Это дополнение к ответу 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 байта)

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

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

Обновления

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

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

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

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

Java Deflater … используемый «сайтом» … C # 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] 

Вы можете использовать модуль 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.

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

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