2010-04-22 16 views
9

Me tropiezo con un problema y no puedo encontrar una solución.Qt quncompress gzip data

Lo que quiero hacer es descomprimir datos en qt, usando qUncompress (QByteArray), enviar desde www en formato gzip. Utilicé wireshark para determinar que esta es una corriente válida de gzip, también probada con zip/rar y que ambos pueden descomprimirla.

Código hasta el momento, es la siguiente:

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; 

Y descompresión falla con: qUncompress: Z_DATA_ERROR: Datos de entrada está dañado.

AFAIK gzip consta de encabezado de 10 bytes, peyload DEFLATE, acoplado de 12 bytes (8 bytes CRC32 + 4 bytes ISIZE - tamaño de datos sin comprimir). El encabezado y el avance del trazado me deberían dejar con la secuencia de datos DEFLATE, qUncompress produce el mismo error.

he comprobado con cadena de datos comprimido en PHP, como este:.

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

y qUncompress descomprimir que los datos (no vi y la cabecera gzip aunque es decir, ID1 = 0x1f, ID2 = 0x8B) I verificado por encima de código con depuración, y el error se produce en:

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

línea inflate.c 610.

sé que qUncompress es simplemente un envoltorio de zlib, por lo que supongamos que debería manejar gzip sin ningún problema. Cualquier comentario es más que bienvenido.

Saludos

+0

¿Qué salida 'qCompress (" {status: false} ")' da y hace que los datos funcionen con qUncompress? Puede dar algunos consejos adicionales sobre lo que está sucediendo. –

+0

QByteArray uncomp = qUncompress (qCompress ("{status: false}")); funciona bien, PHP gzcompress (...) funciona bien, gzip de WWW no funciona. Este {estado: falso} está escrito por WWW, lo descargué en array de wireshark, por lo que estoy seguro de que es una transmisión gzip válida. El único error es que qUncomress dice que esto no es válido. La depuración y el error se producen en ((BITS (8) << 8) + (mantener >> 8))% 31) = verdadero y no deberían ser válidos. – Talei

+0

Fui y escribí mis propias funciones de gzip para trabajar en QByteArrays (usando zlib y GZipHelper.h) – CiscoIPPhone

Respuesta

4

También olvidaron dataPlusSize.append(data);. Sin embargo, eso no resolverá tu problema. El problema es que mientras gzip y zlib tienen el mismo formato de datos comprimidos, sus encabezados y trailers son diferentes. Ver: http://www.zlib.net/zlib_faq.html#faq18

qUncompress utiliza el zlib uncompress, por lo que sólo puede manejar el formato zlib, no el formato gzip. Tendría que llamar a las funciones gzXXXX para manejar el formato gzip.

La razón por la cual qUncompress puede manejar la salida de PHP gzcompress es que gzcompress comprime la cadena dada usando el formato de datos ZLIB. Ver: http://php.net/manual/en/function.gzcompress.php

Como se menciona CiscoIPPhone, tendrá que escribir el suyo propio para manejar datos gzip.

+0

No lo olvidé, es solo error tipográfico aquí en el foro y lo apliqué después del nuevo cálculo de tamaño. ¿No significaría eso que cuando saco el encabezado/tráiler zlib debería descomprimir la secuencia DEFLATE? Porque incluso cuando me señalas que escriba lo mío, en algún momento necesitaré descomprimir los datos de la secuencia DEFLATE con zlib o quncompress. Por cierto. Traté de eliminar datos y solo enviar flujo DEFLATE, también error. Forcé la web para que me diera una respuesta con flujo desinflado también error. – Talei

+0

También en inflate.c hay: if ((state-> wrap & 2) && hold == 0x8b1f) {...}/* gzip header */para mi dat [] es FALSE. ¿Por qué es eso, tengo ID1 e ID2 en 1,2? – Talei

+0

Hice lo que sugirió y fue más fácil de lo que pensé que sería. Gracias. – Talei

8

Usar directamente zlib no es tan difícil.

que he estado haciendo de esta manera:

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

El código se copia desde la página monstly ejemplo de código zlib. Tendrá que include <zlib.h>

+0

según [documentación] (http://www.zlib.net/manual.html) 15 + 32 es una "decodificación zlib y gzip con detección automática de encabezado", mientras que 15 + 16 es un modo para decodificar solo el formato gzip –

6

Aquí es mi aporte ... he desarrollado una clase (QCompressor), en base a zlib para comprimir fácilmente/descomprimir QByteArray usando 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); 
} 

y aquí el main() de mi programa de pruebas:

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

Para permitir que esto funcione, es necesario añadir una línea LIBS += -lz a su .pro archivo f o enlazando contra zlib.