2010-04-22 4 views
9

Ich stolpere auf ein Problem, und kann keine Lösung finden.Qt qzip-Daten komprimieren

Also was ich tun möchte, ist Daten in Qt dekomprimieren, mit qUncompress (QByteArray), senden von www im Gzip-Format. Ich habe wireshark verwendet, um zu bestimmen, dass dies ein gültiger gzip-Stream ist, der auch mit zip/rar getestet wurde und beide ihn dekomprimieren können.

-Code so weit ist wie folgt aus:

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; 

und Dekomprimierung nicht mit: qUncompress: Z_DATA_ERROR: Eingangsdaten beschädigt ist.

AFAIK gzip besteht aus 10 Byte Header, DEFLATE Peyload, 12 Byte Trailer (8 Byte CRC32 + 4 Byte ISIZE - nicht komprimierte Datengröße). Striping Header und Trailer sollte mich mit DEFLATE Datenstrom verlassen, qUncompress liefert den gleichen Fehler.

I mit Datenfolge überprüft in PHP komprimiert, wie folgt aus:.

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

und qUncompress dekomprimieren, dass Daten (I nicht und gzip-Header nicht sehen, obwohl dh ID1 = 0x1f, ID2 = 0x8B) I geprüft obigen Code mit debuggen und Fehler tritt auf:

 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 Linie 610.

ich weiß, dass qUncompress einfach ein Wrapper zlib, so dass ich Angenommen, es sollte mit gzip ohne Probleme umgehen. Alle Kommentare sind mehr als willkommen.

Mit freundlichen Grüßen

+0

Welche Ausgabe gibt 'qCompress (" {status: false} ")' und gibt diese Daten dann mit qUncompress? Kann einige zusätzliche Hinweise geben, was vor sich geht. –

+0

QByteArray uncomp = qUncompress (qCompress ("{status: false}")); funktioniert gut, PHP gzcompress (...) funktioniert gut, gzip aus dem WWW funktioniert nicht. Dieser {status: false} wird von WWW geziped, ich habe ihn von wireshark nach array geworfen, daher bin ich sicher, dass es ein gültiger gzip-Stream ist. Einziger Fehler ist, dass qUncomress sagt, dass dies ungültig ist. Traced debug und error tritt bei ((BITS (8) << 8) + (hold >> 8))% 31) = true auf und sollte nicht für einen gültigen Stream verwendet werden. – Talei

+0

Ich ging und schrieb meine eigenen gzip-Funktionen, um auf QByteArrays zu arbeiten (mit zlib und GZipHelper.h) – CiscoIPPhone

Antwort

4

Sie haben vergessen, auch dataPlusSize.append(data);. Das wird jedoch Ihr Problem nicht lösen. Das Problem ist, dass, während gzip und zlib das gleiche komprimierte Datenformat haben, ihre Header und Trailer unterschiedlich sind. Siehe: http://www.zlib.net/zlib_faq.html#faq18

qUncompress verwendet die zlib , so kann es nur das zlib-Format, nicht das Gzip-Format verarbeiten. Es müsste die gzXXXX Funktionen aufrufen, um das gzip-Format zu behandeln.

Der Grund, dass qUncompress Ausgabe von PHP gzcompress verarbeiten kann, ist, dass gzcompress die angegebene Zeichenfolge komprimiert, die das ZLIB-Datenformat verwendet. Siehe: http://php.net/manual/en/function.gzcompress.php

Wie CiscoIPPhone erwähnt, müssen Sie Ihre eigenen zu Funktionen schreiben, um gzip Daten zu behandeln.

+0

Ich habe nicht vergessen, es ist nur Tippfehler hier im Forum und ich apps es nach neuer Größenberechnung. Würde das nicht bedeuten, wenn ich Header/Trailer zlib ziehe sollte DEFLATE Stream dekomprimieren? Denn selbst wenn Sie mich auf das Schreiben meiner eigenen Sache hinweisen, muss ich irgendwann die Daten des DEFLATE-Streams entweder mit zlib oder mit quncompress dekomprimieren. Übrigens. Ich habe versucht, Daten zu strippen und nur DEFLATE stream, auch Fehler zu senden. Ich habe gezwungen, mich mit deflate stream zu beantworten. – Talei

+0

Auch in der inflate.c gibt es: if ((state-> wrap & 2) && halten == 0x8b1f) {...}/* gzip header */für meine dat [] ist es FALSE. Warum habe ich ID1 & ID2 bei 1,2? – Talei

+0

Ich habe getan, was Sie vorgeschlagen haben und es war einfacher als ich dachte, es wäre. Danke. – Talei

8

Direkt mit zlib ist nicht so schwer.

Ich habe es wie folgt tun:

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

Der Code wird monstly vom zlib Codebeispiel Seite kopiert. Sie müssen include <zlib.h>

+0

nach [Dokumentation] (http://www.zlib.net/manual.html) 15 + 32 ist eine "zlib und gzip Decodierung mit automatischer Header-Erkennung", während 15 + 16 ist ein Modus zum Decodieren nur gzip-Format –

6

mein Beitrag hier ... Ich habe eine Klasse entwickelt haben (QCompressor), basierend auf zlib für leicht zu komprimieren/dekomprimieren QByteArray GZIP verwenden.

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

und hier die main() meines Testprogramm:

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

Für diese Arbeit lassen, müssen Sie eine Zeile hinzufügen LIBS += -lz zu Ihrer .pro Datei f oder verlinken gegen zlib.