2010-04-22 6 views
9

Je tombe sur un problème et ne trouve pas de solution.Qt quncompress données gzip

Donc, ce que je veux faire est de décompresser les données en qt, en utilisant qUncompress (QByteArray), envoyer à partir de www en format gzip. J'ai utilisé wireshark pour déterminer que c'est un flux gzip valide, également testé avec zip/rar et les deux peuvent le décompresser.

jusqu'à présent code, est comme ceci:

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; 

Et échoue avec uncompression: qUncompress: Z_DATA_ERROR: les données d'entrée est corrompu. AFAIK gzip se compose d'un en-tête de 10 octets, d'un peyload DEFLATE, d'une bande-annonce de 12 octets (CRC32 de 8 octets + ISIZE de 4 octets - taille de données non comprimée). L'en-tête de bande et la bande-annonce doivent me laisser avec le flux de données DEFLATE, qUncompress génère la même erreur.

J'ai vérifié avec chaîne de données compressées en PHP, comme ceci:.

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

et qUncompress Décompressez que les données (je ne voyais pas et tête gzip bien-à-dire ID1 = 0x1f, ID2 = 0x8b) I vérifié code ci-dessus avec le débogage et l'erreur se produit à:

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

ligne inflate.c 610.

Je sais que qUncompress est tout simplement un emballage à zlib, donc je supposons qu'il devrait gérer gzip sans aucun problème. Tous les commentaires sont plus que bienvenus.

Meilleures salutations

+0

Quelle est la sortie de 'qCompress (" {status: false} ")' donne et fait fonctionner ces données avec qUncompress? Peut donner quelques indications supplémentaires sur ce qui se passe. –

+0

QByteArray uncomp = qUncompress (qCompress ("{status: false}")); fonctionne bien, PHP gzcompress (...) fonctionne bien, gzip de WWW ne fonctionne pas. Ce {status: false} est gzipé par WWW, je l'ai jeté à array de wireshark, donc je suis positif que c'est un flux gzip valide. Seule l'erreur est que qUncomress indique que ceci est invalide. Le débogage et l'erreur tracés se produisent à ((BITS (8) << 8) + (maintenez >> 8))% 31) = true et ne devraient pas pour le flux valide. – Talei

+0

Je suis allé et a écrit mes propres fonctions gzip pour travailler sur QByteArrays (en utilisant zlib et GZipHelper.h) – CiscoIPPhone

Répondre

4

Vous avez également oublié dataPlusSize.append(data);. Cependant, cela ne résoudra pas votre problème. Le problème est que même si gzip et zlib ont le même format de données compressé, leurs en-têtes et leurs bandes-annonces sont différents. Voir: http://www.zlib.net/zlib_faq.html#faq18

qUncompress utilise le zlib uncompress, donc il ne peut gérer que le format zlib, pas le format gzip. Il aurait besoin d'appeler les fonctions gzXXXX pour gérer le format gzip.

La raison pour laquelle qUncompress peut gérer la sortie de PHP gzcompress est que gzcompress compresse la chaîne donnée en utilisant le format de données ZLIB. Voir: http://php.net/manual/en/function.gzcompress.php

Comme CiscoIPPhone mentionné, vous devrez écrire vos propres fonctions pour gérer les données gzip.

+0

Je n'ai pas oublié, il est seulement typo ici sur le forum et je l'ai apposé après un nouveau calcul de taille. Cela ne veut-il pas dire, quand je bande l'en-tête/la bande-annonce zlib devrait décompresser le flux DEFLATE? Parce que même lorsque vous me pointez pour écrire ma propre chose, à un moment donné, je devrai décompresser les données de flux DEFLATE soit avec zlib ou quncompress. BTW. J'ai essayé de dépouiller les données et seulement envoyer le flux DEFLATE, aussi erreur. J'ai forcé le web à me donner une réponse avec déflate stream aussi erreur. – Talei

+0

Toujours dans inflate.c il y a: if ((state-> wrap & 2) && hold == 0x8b1f) {...}/* gzip header */pour mon dat [] c'est FAUX. Pourquoi est-ce, j'ai ID1 & ID2 à 1,2? – Talei

+0

J'ai fait ce que vous avez suggéré et c'était plus facile que je pensais que ce serait. Merci. – Talei

8

L'utilisation directe de zlib n'est pas si difficile.

Je l'ai fait comme ceci:

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

Le code est copié monstly de la page exemple de code zlib. Vous devrez include <zlib.h>

+0

d'après [documentation] (http://www.zlib.net/manual.html) 15 + 32 est un "décodage zlib et gzip avec détection automatique d'en-tête" alors que 15 + 16 est un mode de décodage de format gzip uniquement –

6

Voici ma contribution ... J'ai développé une classe (QCompressor), sur la base zlib pour facilement compresser/décompresser QByteArray avec 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); 
} 

et ici le main() de mon programme de 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"; 
} 

Pour faire ce travail, vous devez ajouter une ligne LIBS += -lz à votre fichier .pro f ou lier contre zlib.