2010-04-22 18 views
9

ho inciampare su un problema, e non riescono a trovare una soluzione.Qt quncompress dati gzip

Quindi quello che voglio fare è decomprimere i dati in qt, usando qUncompress (QByteArray), inviare da www in formato gzip. Ho usato wireshark per determinare che questo è valido per gzip stream, anche testato con zip/rar ed entrambi possono decomprimerlo.

codice finora, è come questo:

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; 

E decompressione fallisce con: qUncompress: Z_DATA_ERROR: Dati di ingresso è danneggiato.

AFAIK gzip consiste di 10 byte header, DEFLATE peyload, 12 byte trailer (8 byte CRC32 + 4 byte ISIZE - dimensione dati non rappresentati). L'intestazione e il trailer di striping dovrebbero lasciarmi con il flusso di dati DEFLATE, qUncompress restituisce lo stesso errore.

Ho controllato con stringa di dati compressi in PHP, in questo modo:.

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

e qUncompress decomprimere i dati (non ho visto e intestazione gzip se cioè ID1 = 0x1F, ID2 = 0x8b) I verificata sopra il codice con il debug, e l'errore si verifica in:

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

linea inflate.c 610.

so che qUncompress è semplicemente un wrapper per zlib, così ho supponiamo che dovrebbe gestire gzip senza alcun problema. Qualsiasi commento è più che benvenuto.

migliori saluti

+0

Quale output fa 'qCompress (" {status: falso} ")' fornisce e fa quei dati quindi funziona con qUncompress? Può dare qualche suggerimento in più su cosa sta succedendo. –

+0

QByteArray uncomp = qUncompress (qCompress ("{stato: falso}")); funziona bene, PHP gzcompress (...) funziona bene, gzip di WWW non funziona. Questo {status: falso} è gzipato da WWW, l'ho scaricato su array da wireshark, quindi sono positivo che sia valido per gzip stream. L'unico errore è che qUncomress dice che questo non è valido. Il debug e l'errore di tracciamento si verificano in ((BITS (8) << 8) + (premuto >> 8))% 31) = true e non dovrebbero per flusso valido. – Talei

+0

Sono andato e ho scritto le mie funzioni gzip per lavorare su QByteArrays (usando zlib e GZipHelper.h) – CiscoIPPhone

risposta

4

È inoltre dimenticato dataPlusSize.append(data);. Tuttavia, ciò non risolverà il tuo problema. Il problema è che mentre gzip e zlib hanno lo stesso formato di dati compresso, le intestazioni e i rimorchi sono diversi. Vedi: http://www.zlib.net/zlib_faq.html#faq18

qUncompress utilizza lo zlib uncompress, quindi può gestire solo il formato zlib, non il formato gzip. Dovrebbe chiamare le funzioni gzXXXX per gestire il formato gzip.

La ragione per cui qUncompress è in grado di gestire l'output da gzcompress di PHP è che gzcompress comprime la stringa data utilizzando il formato dati ZLIB. Vedi: http://php.net/manual/en/function.gzcompress.php

Come menzionato da CiscoIPPhone, è necessario scriverne le proprie per gestire i dati gzip.

+0

Non ho dimenticato, è solo errore di battitura qui sul forum e l'ho appato dopo il calcolo della nuova dimensione. Non significherebbe, quando toglietto header/trailer, zlib dovrebbe decomprimere il flusso DEFLATE? Perché anche quando mi punti a scrivere la mia cosa, a un certo punto dovrò decomprimere i dati del flusso DEFLATE con zlib o quncompress. BTW. Ho provato a spogliare i dati e inviare solo flusso DEFLATE, anche errore. Ho forzato la rete a darmi anche la risposta con il flusso di deflate. – Talei

+0

Anche in inflate.c c'è: if ((state-> wrap & 2) && hold == 0x8b1f) {...}/* gzip header */per my dat [] è FALSE. Perché, ho ottenuto ID1 e ID2 a 1,2? – Talei

+0

Ho fatto quello che mi hai suggerito ed è stato più facile allora pensavo che sarebbe stato. Grazie. – Talei

8

Direttamente usando zlib non è così difficile.

ho fatto in questo modo:

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

il codice viene monstly copiato dall'esempio pagina di codice zlib. Sarà necessario include <zlib.h>

+0

in base alla [documentazione] (http://www.zlib.net/manual.html) 15 + 32 è una "decodifica zlib e gzip con rilevamento automatico dell'intestazione" mentre 15 + 16 è una modalità per decodificare solo il formato gzip –

6

Ecco il mio contributo ... Ho sviluppato una classe (QCompressor), sulla base di zlib per facilmente comprimere/decomprimere 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); 
} 

e qui il main() del mio programma di test:

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

Per far questo lavoro, è necessario aggiungere una riga LIBS += -lz al tuo file .pro f o collegamento zlib.