2009-07-06 4 views
40

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

Я знаю, что Python включает в себя Zlib, и что библиотеки C в Zlib обращений в службу поддержки на Накачайте и выкачивает, но они, по-видимому, не обеспечиваются модулем Python Zlib. Это действительно обеспечивает Сжать и Decompress, но когда я делаю вызов, такие как:

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 

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

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

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

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

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

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

7b0HYBxJliUmL23Ke39K9UrX4HShCIBgEyTYkEAQ7MGIzeaS7B1pRyMpqyqBymVWZV1mFkDM7Z28995777333nvvvfe6O51OJ/ff/z9cZmQBbPbOStrJniGAqsgfP358Hz8iZvl5mbV5mi1nab6cVrM8XeT/Dw== 

Когда «выкачать и закодировать меня» запускается через 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 и раздувать, без заголовка и контрольной суммы, следующие вещи, необходимые для произойти:

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

Надувание/распаковка: есть второй аргумент для размера окна. Если это значение отрицательное, оно подавляет заголовки. вот мои методы в настоящее время, в том числе кодирования 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) 

ответ

16

Это дополнение к ответу 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 работает над (как вы уже догадались) спускающимся потоком. Код MizardX сообщает модулю zlib, что данные являются сырым потоком дефлята.

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

Обновление

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

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

См How do you use a DeflateStream on part of a file?

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

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

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.

однострочный Deflater после выбрасывая 2-байтовый ZLIB заголовок и 4-байтовый контрольную сумму:

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

или

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

+1 Спасибо за дополнительную информацию. – Demi

+0

@John Machin: Чтобы ответить на ваше первое наблюдение ... результат будет только дольше в случае более коротких строк (header? Padding?). Когда я подаю 161 байт данных для дефляции, до кодировки base64 результат составляет 126 байтов. – Demi

+0

@John Machin: Отличная информация и информация. Явная подпись используемого дефлатера - это одна с двумя параметрами, с nowrap == true. Я использовал ваш пример однострочного дефлатера, и он хорошо накапливается в .NET и Java, несмотря на то, что он отличается от значения, вызванного дефляцией с библиотеками на этих языках. Отлично. Теперь я работаю над инфляцией - принимая дефлированные данные, созданные Java или .NET, и добавляя контрольную сумму adler32 и заголовок zlib, чтобы увидеть, могу ли я заставить Python хорошо ее использовать. Я дам вам знать, как это происходит. – Demi

17

Вы все еще можете использовать 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С
zlib.compressobj(...)deflateInit(...)
compressobj.compress(...)deflate(...)
zlib.decompressobj(...)inflateInit(...)
decompressobj.decompress(...)inflate(...)

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

+0

Я ищу for - это доступ к C-level Inflate и Deflate вызовам библиотеки, которые модуль Python Zlib завершает. Не похоже, что Decompress и Compress делают то же самое, и модуль Python Zlib не выставляет Inflate и Deflate – Demi

+0

Это не полезно. Обратите внимание на дополнительную информацию, добавленную мной на мой вопрос выше. Код, который вы указали выше, при запуске со строкой «deflate and encode me», приводит к «S0lNy0ksSVVIzEtRSM1Lzk9JVchNBQA =», что еще короче. Правильный результат Deflate должен выглядеть как (более длинная) строка .NET, которую я отмечаю выше. – Demi

+0

Как строка ввода из 21 символа приводит к дефлятированному выходу на 212 байтов? Включает ли это заголовок файла deflate? –