Qt quncompress gzip data - PullRequest
       19

Qt quncompress gzip data

10 голосов
/ 22 апреля 2010

Я наткнулся на проблему и не могу найти решение.

Итак, я хочу распаковать данные в qt, используя qUncompress (QByteArray), отправленный с www в формате gzip. Я использовал wireshark, чтобы определить, что это действительный поток gzip, также протестированный с zip / rar, и оба могут распаковать его.

Код до сих пор, выглядит так:

    static const char dat[40] = {
         0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xaa, 0x2e, 0x2e, 0x49, 0x2c, 0x29,
         0x2d, 0xb6, 0x4a, 0x4b, 0xcc, 0x29, 0x4e, 0xad, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00,
         0x2a, 0x63, 0x18, 0xc5, 0x0e, 0x00, 0x00, 0x00
    };
//this data contains string: {status:false}, in gzip format
QByteArray data;
           data.append( dat, sizeof(dat) );

unsigned int size = 14; //expected uncompresed size, reconstruct it BigEndianes

//prepand expected uncompressed size, last 4 byte in dat 0x0e = 14
QByteArray dataPlusSize;

dataPlusSize.append( (unsigned int)((size >> 24) & 0xFF));
dataPlusSize.append( (unsigned int)((size >> 16) & 0xFF));
dataPlusSize.append( (unsigned int)((size >> 8) & 0xFF));
dataPlusSize.append( (unsigned int)((size >> 0) & 0xFF));

QByteArray uncomp = qUncompress( dataPlusSize );
qDebug() << uncomp;

И распаковка завершается неудачно с: qUncompress: Z_DATA_ERROR: входные данные повреждены.

AFAIK gzip состоит из 10-байтового заголовка, peyload DEFLATE, 12-байтового трейлера (8-байтовый CRC32 + 4-байтовый ISIZE - размер неосвоенных данных). Разбор заголовка и трейлера должен оставить меня с потоком данных DEFLATE, qUncompress выдает ту же ошибку.

Я проверил с помощью сжатой строки данных в PHP, например:

$stringData = gzcompress( "{status:false}", 1);

и qUncompress распаковывают эти данные (хотя я не видел и gzip заголовок, то есть ID1 = 0x1f, ID2 = 0x8b) Я проверил приведенный выше код с отладкой, и ошибка происходит в:

        if (
        #endif
            ((BITS(8) << 8) + (hold >> 8)) % 31) { //here is error, WHY? long unsigned int hold = 35615
            strm->msg = (char *)"incorrect header check";
            state->mode = BAD;
            break;
        } 

inflate.c, строка 610.

Я знаю, что qUncompress - это просто оболочка для zlib, поэтому я полагаю, что он должен обрабатывать gzip без проблем. Любые комментарии приветствуются.

С уважением

Ответы [ 3 ]

11 голосов
/ 25 июля 2014

Вот мой вклад ... Я разработал класс (QCompressor), основанный на zlib для простого сжатия / распаковки QByteArray с использованием GZIP.

qcompressor.h

#ifndef QCOMPRESSOR_H
#define QCOMPRESSOR_H

#include <zlib.h>
#include <QByteArray>

#define GZIP_WINDOWS_BIT 15 + 16
#define GZIP_CHUNK_SIZE 32 * 1024

class QCompressor
{
public:
    static bool gzipCompress(QByteArray input, QByteArray &output, int level = -1);
    static bool gzipDecompress(QByteArray input, QByteArray &output);
};

#endif // QCOMPRESSOR_H

qcompressor.cpp:

#include "qcompressor.h"

/**
 * @brief Compresses the given buffer using the standard GZIP algorithm
 * @param input The buffer to be compressed
 * @param output The result of the compression
 * @param level The compression level to be used (@c 0 = no compression, @c 9 = max, @c -1 = default)
 * @return @c true if the compression was successful, @c false otherwise
 */
bool QCompressor::gzipCompress(QByteArray input, QByteArray &output, int level)
{
    // Prepare output
    output.clear();

    // Is there something to do?
    if(input.length())
    {
        // Declare vars
        int flush = 0;

        // Prepare deflater status
        z_stream strm;
        strm.zalloc = Z_NULL;
        strm.zfree = Z_NULL;
        strm.opaque = Z_NULL;
        strm.avail_in = 0;
        strm.next_in = Z_NULL;

        // Initialize deflater
        int ret = deflateInit2(&strm, qMax(-1, qMin(9, level)), Z_DEFLATED, GZIP_WINDOWS_BIT, 8, Z_DEFAULT_STRATEGY);

        if (ret != Z_OK)
            return(false);

        // Prepare output
        output.clear();

        // Extract pointer to input data
        char *input_data = input.data();
        int input_data_left = input.length();

        // Compress data until available
        do {
            // Determine current chunk size
            int chunk_size = qMin(GZIP_CHUNK_SIZE, input_data_left);

            // Set deflater references
            strm.next_in = (unsigned char*)input_data;
            strm.avail_in = chunk_size;

            // Update interval variables
            input_data += chunk_size;
            input_data_left -= chunk_size;

            // Determine if it is the last chunk
            flush = (input_data_left <= 0 ? Z_FINISH : Z_NO_FLUSH);

            // Deflate chunk and cumulate output
            do {

                // Declare vars
                char out[GZIP_CHUNK_SIZE];

                // Set deflater references
                strm.next_out = (unsigned char*)out;
                strm.avail_out = GZIP_CHUNK_SIZE;

                // Try to deflate chunk
                ret = deflate(&strm, flush);

                // Check errors
                if(ret == Z_STREAM_ERROR)
                {
                    // Clean-up
                    deflateEnd(&strm);

                    // Return
                    return(false);
                }

                // Determine compressed size
                int have = (GZIP_CHUNK_SIZE - strm.avail_out);

                // Cumulate result
                if(have > 0)
                    output.append((char*)out, have);

            } while (strm.avail_out == 0);

        } while (flush != Z_FINISH);

        // Clean-up
        (void)deflateEnd(&strm);

        // Return
        return(ret == Z_STREAM_END);
    }
    else
        return(true);
}

/**
 * @brief Decompresses the given buffer using the standard GZIP algorithm
 * @param input The buffer to be decompressed
 * @param output The result of the decompression
 * @return @c true if the decompression was successfull, @c false otherwise
 */
bool QCompressor::gzipDecompress(QByteArray input, QByteArray &output)
{
    // Prepare output
    output.clear();

    // Is there something to do?
    if(input.length() > 0)
    {
        // Prepare inflater status
        z_stream strm;
        strm.zalloc = Z_NULL;
        strm.zfree = Z_NULL;
        strm.opaque = Z_NULL;
        strm.avail_in = 0;
        strm.next_in = Z_NULL;

        // Initialize inflater
        int ret = inflateInit2(&strm, GZIP_WINDOWS_BIT);

        if (ret != Z_OK)
            return(false);

        // Extract pointer to input data
        char *input_data = input.data();
        int input_data_left = input.length();

        // Decompress data until available
        do {
            // Determine current chunk size
            int chunk_size = qMin(GZIP_CHUNK_SIZE, input_data_left);

            // Check for termination
            if(chunk_size <= 0)
                break;

            // Set inflater references
            strm.next_in = (unsigned char*)input_data;
            strm.avail_in = chunk_size;

            // Update interval variables
            input_data += chunk_size;
            input_data_left -= chunk_size;

            // Inflate chunk and cumulate output
            do {

                // Declare vars
                char out[GZIP_CHUNK_SIZE];

                // Set inflater references
                strm.next_out = (unsigned char*)out;
                strm.avail_out = GZIP_CHUNK_SIZE;

                // Try to inflate chunk
                ret = inflate(&strm, Z_NO_FLUSH);

                switch (ret) {
                case Z_NEED_DICT:
                    ret = Z_DATA_ERROR;
                case Z_DATA_ERROR:
                case Z_MEM_ERROR:
                case Z_STREAM_ERROR:
                    // Clean-up
                    inflateEnd(&strm);

                    // Return
                    return(false);
                }

                // Determine decompressed size
                int have = (GZIP_CHUNK_SIZE - strm.avail_out);

                // Cumulate result
                if(have > 0)
                    output.append((char*)out, have);

            } while (strm.avail_out == 0);

        } while (ret != Z_STREAM_END);

        // Clean-up
        inflateEnd(&strm);

        // Return
        return (ret == Z_STREAM_END);
    }
    else
        return(true);
}

и вот main() моей тестовой программы:

#include <QDebug>
#include "qcompressor.h"

int main(int argc, char *argv[])
{
    Q_UNUSED(argc);
    Q_UNUSED(argv);

    QString initialPlainText = "This is a test program for verifying that the QCompressor class works fine!";

    qDebug() << "Initial plain text is: " << initialPlainText;

    QByteArray compressed;

    if(QCompressor::gzipCompress(initialPlainText.toLatin1(), compressed))
    {
        qDebug() << "Compressed text length is:" << compressed.length();

        QByteArray decompressed;

        if(QCompressor::gzipDecompress(compressed, decompressed))
        {
            qDebug() << "Decompressed text is: " << QString::fromLatin1(decompressed);
        }
        else
            qDebug() << "Can't decompress";
    }
    else
        qDebug() << "Can't compress";
}

Чтобы позволить этой работе, вам нужно добавить строку LIBS += -lz в ваш файл .pro для связи с zlib.

8 голосов
/ 08 сентября 2011

Непосредственное использование zlib не так сложно.

Я делал это так:

QByteArray gUncompress(const QByteArray &data)
{
    if (data.size() <= 4) {
        qWarning("gUncompress: Input data is truncated");
        return QByteArray();
    }

    QByteArray result;

    int ret;
    z_stream strm;
    static const int CHUNK_SIZE = 1024;
    char out[CHUNK_SIZE];

    /* allocate inflate state */
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.avail_in = data.size();
    strm.next_in = (Bytef*)(data.data());

    ret = inflateInit2(&strm, 15 +  32); // gzip decoding
    if (ret != Z_OK)
        return QByteArray();

    // run inflate()
    do {
        strm.avail_out = CHUNK_SIZE;
        strm.next_out = (Bytef*)(out);

        ret = inflate(&strm, Z_NO_FLUSH);
        Q_ASSERT(ret != Z_STREAM_ERROR);  // state not clobbered

        switch (ret) {
        case Z_NEED_DICT:
            ret = Z_DATA_ERROR;     // and fall through
        case Z_DATA_ERROR:
        case Z_MEM_ERROR:
            (void)inflateEnd(&strm);
            return QByteArray();
        }

        result.append(out, CHUNK_SIZE - strm.avail_out);
    } while (strm.avail_out == 0);

    // clean up and return
    inflateEnd(&strm);
    return result;
}

Код чудовищно скопирован со страницы примера кода zlib.Вам нужно будет include <zlib.h>

5 голосов
/ 22 апреля 2010

Ты тоже забыл dataPlusSize.append(data);.Однако это не решит вашу проблему.Проблема в том, что, хотя gzip и zlib имеют одинаковый формат сжатых данных, их заголовки и трейлеры различаются.См .: http://www.zlib.net/zlib_faq.html#faq18

qUncompress использует zlib uncompress, поэтому он может обрабатывать только формат zlib, но не формат gzip.Для обработки формата gzip потребуется вызвать функции gzXXXX.

Причина, по которой qUncompress может обрабатывать выходные данные PHP gzcompress, заключается в том, что gzcompress сжимает заданную строку с использованием формата данных ZLIB.См .: http://php.net/manual/en/function.gzcompress.php

Как уже упоминалось в CiscoIPPhone, вам нужно написать свои собственные функции для обработки данных gzip.

...