2011-06-26 8 views
16

私はその場で圧縮と圧縮解除を行う必要があります。 Qtでqnetworkmanagerを使用して大きなファイルを受信して​​います。データストリームのダウンロード中に解凍する必要があります。C++ lzma大きなストリームを部分単位で圧縮および解凍する

私はデータストリームの一部を受け取ったとき、データを解凍し、ファイルに追加し、プロセス中に使用済みのメモリを解放する必要があります。これを行う最善の方法は何ですか?

今、私はxz-utils、純粋なc apiを試しています。誰かがより良い方法を提案できるのでしょうか? this例に基づいてコード次

UPD2:

extern "C" void *lz_alloc(void *opaque, size_t nmemb, size_t size) 
{ 
void *p = NULL; 
try{ 
    p = new char [size]; 
} 
catch(std::bad_alloc &ba) 
{ 
    p = NULL; 
} 
return p; 
} 

extern "C" void lz_free(void *opaque, void *ptr) 
{ 
delete [] (char*)ptr; 
} 


QByteArray lzCompress(QByteArray data) 
{ 
QByteArray arr; 
lzma_check check = LZMA_CHECK_CRC64; 
lzma_stream strm = LZMA_STREAM_INIT; /* alloc and init lzma_stream struct */ 
lzma_allocator al; 
al.alloc = lz_alloc; 
al.free = lz_free; 
strm.allocator = &al; 
byte *in_buf; 
byte out_buf [OUT_BUF_MAX]; 
size_t in_len; /* length of useful data in in_buf */ 
size_t out_len; /* length of useful data in out_buf */ 
lzma_ret ret_xz; 

/* initialize xz encoder */ 
ret_xz = lzma_easy_encoder (&strm, 9 | LZMA_PRESET_EXTREME, check); 
if (ret_xz != LZMA_OK) { 
    return QByteArray(); 
} 

in_len = data.size(); 
in_buf = (byte*)data.data(); 
strm.next_in = in_buf; 
strm.avail_in = in_len; 

do { 
    strm.next_out = out_buf; 
    strm.avail_out = OUT_BUF_MAX; 
    ret_xz = lzma_code (&strm, LZMA_FINISH); 

    out_len = OUT_BUF_MAX - strm.avail_out; 
    arr.append((char*)out_buf, out_len); 
    out_buf[0] = 0; 
} while (strm.avail_out == 0); 
lzma_end (&strm); 
return arr; 
} 

私はsleeped数時間を持って、そして今私はそれを更新し、私の間違ったコードを修正し、より明確に考えて(それは同じように動作しますqCompress作品)

UPD3:

伸長コード(行動のようなqUncompress)

QByteArray lzUncompress(QByteArray data) 
{ 
lzma_stream strm = LZMA_STREAM_INIT; /* alloc and init lzma_stream struct */ 
const uint32_t flags = LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED; 
const uint64_t memory_limit = UINT64_MAX; /* no memory limit */ 
byte *in_buf; 
uint8_t out_buf [OUT_BUF_MAX]; 
size_t in_len; /* length of useful data in in_buf */ 
size_t out_len; /* length of useful data in out_buf */ 
lzma_ret ret_xz; 
QByteArray arr; 

ret_xz = lzma_stream_decoder (&strm, memory_limit, flags); 
if (ret_xz != LZMA_OK) { 
    return QByteArray(); 
} 

in_len = data.size(); 
in_buf = (byte*)data.data(); 

strm.next_in = in_buf; 
strm.avail_in = in_len; 
do { 
    strm.next_out = out_buf; 
    strm.avail_out = OUT_BUF_MAX; 
    ret_xz = lzma_code (&strm, LZMA_FINISH); 

    out_len = OUT_BUF_MAX - strm.avail_out; 
    arr.append((char*)out_buf, out_len); 
    out_buf[0] = 0; 
} while (strm.avail_out == 0); 
lzma_end (&strm); 
return arr; 
} 

UPD4:

基本ストリーム解凍クラス、コード以下はちょうど私が必要とする、まさに、その場でhttpサーバからダウンロードしたXZストリームを解凍:

class lz_stream_decompressor : public QObject 
{ 
Q_OBJECT 
public: 
lz_stream_decompressor(QNetworkReply *r, QNetworkAccessManager *q, const QString &str, unsigned long sz): flags(LZMA_TELL_UNSUPPORTED_CHECK | LZMA_CONCATENATED), memory_limit(UINT64_MAX), state(0), total_upd_size(sz) 
{ 
    repl = r; 
    qnm = q; 
    path = str; 
    strm.next_in = NULL; 
    strm.avail_in = 0; 
    strm.total_in = 0; 
    strm.next_out = NULL; 
    strm.avail_out = 0; 
    strm.total_out = 0; 
    strm.allocator = NULL; 
    strm.internal = NULL; 
    strm.reserved_ptr1 = NULL; 
    strm.reserved_ptr2 = NULL; 
    strm.reserved_ptr3 = NULL; 
    strm.reserved_ptr4 = NULL; 
    strm.reserved_int1 = 0; 
    strm.reserved_int2 = 0; 
    strm.reserved_int3 = 0; 
    strm.reserved_int4 = 0; 
    strm.reserved_enum1 = LZMA_RESERVED_ENUM; 
    strm.reserved_enum2 = LZMA_RESERVED_ENUM; 
    ret_xz = lzma_stream_decoder (&strm, memory_limit, flags); 
    if (ret_xz != LZMA_OK) 
    { 
     state = -1; 
     repl->abort(); 
    } 
    else 
    { 
     connect(repl, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handle_new_data(qint64,qint64))); 
     connect(q, SIGNAL(finished(QNetworkReply*)), SLOT(compressed_file_request_finished(QNetworkReply*))); 
     QFile(path).rename(path + ".tmp"); 
    } 
} 
~lz_stream_decompressor() 
{ 
/*  if(repl) 
     delete repl; */ 
    lzma_end (&strm); 
} 
const short get_state() 
{ 
    return state; 
} 
signals: 
void finished(); 

public slots: 
void handle_new_data(qint64 bytesReceived, qint64 bytesTotal); 
void compressed_file_request_finished(QNetworkReply*); 
private: 
QNetworkReply *repl; 
QNetworkAccessManager *qnm; 
lzma_stream strm; 
const uint32_t flags; 
const uint64_t memory_limit; /* no memory limit */ 
short state; 
byte *in_buf; 
byte out_buf [OUT_BUF_MAX]; 
size_t in_len; /* length of useful data in in_buf */ 
size_t out_len; /* length of useful data in out_buf */ 
lzma_ret ret_xz; 
QString path; 
unsigned long &total_upd_size; 
}; 

と実現:

void lz_stream_decompressor::handle_new_data(qint64 bytesReceived, qint64 bytesTotal) 
{ 
if(repl->error() != QNetworkReply::NoError) 
{//TODO: handle error here 
    QFile(path).remove(); 
    QFile(path + ".tmp").rename(path); 
    return; 
} 
total_upd_size -= repl->bytesAvailable(); 
QByteArray data = repl->readAll(); 
in_len = data.size(); 
in_buf = (byte*)data.data(); 
strm.next_in = in_buf; 
strm.avail_in = in_len; 

do { 
    strm.next_out = out_buf; 
    strm.avail_out = OUT_BUF_MAX; 
    ret_xz = lzma_code (&strm, LZMA_RUN); 
    out_len = OUT_BUF_MAX - strm.avail_out; 
    QFile file(path); 
    if(file.open(QIODevice::WriteOnly | QIODevice::Append)) 
    { 
     file.write(QByteArray((char*)out_buf, (int)out_len)); 
     file.close(); 
    } 
    out_buf[0] = 0; 
} while (strm.avail_out == 0); 
} 

void lz_stream_decompressor::compressed_file_request_finished(QNetworkReply* repl) 
{ 
if(repl->error() != QNetworkReply::NoError) 
{//TODO: handle error here 
    QFile(path).remove(); 
    QFile(path + ".tmp").rename(path); 
    emit finished(); 
    return; 
} 
total_upd_size -= repl->bytesAvailable(); 
QByteArray data = repl->readAll(); 
in_len = data.size(); 
in_buf = (byte*)data.data(); 
strm.next_in = in_buf; 
strm.avail_in = in_len; 

do { 
    strm.next_out = out_buf; 
    strm.avail_out = OUT_BUF_MAX; 
    ret_xz = lzma_code (&strm, LZMA_FINISH); 
    out_len = OUT_BUF_MAX - strm.avail_out; 
    QFile file(path); 
    if(file.open(QIODevice::WriteOnly | QIODevice::Append)) 
    { 
     file.write(QByteArray((char*)out_buf, (int)out_len)); 
     file.close(); 
    } 
    out_buf[0] = 0; 
} while (strm.avail_out == 0); 
repl->deleteLater(); 
QFile(path + ".tmp").remove(); 
emit finished(); 
} 

これはすべて、最初のリンクの例に基づいています。非圧縮データで何かを行うには、コメント付きのコード部分をコードに置き換える必要があります。

私はあなたにも非圧縮データを完了するためにqnetworkmanagerの信号を終えする「compressed_file_request_finished」スロットを接続する必要があり、このコード

に任意の提案を見たいのですが。

UPD5:lzCompressとlzUncompressを固定

、私はこれを読んで、handle_new_dataでLZMA_FULL_FLUSHを使用する方法について確認してください、今も元気ない作業のように見えますが、今私は、既存のコードを適応させる、必ず私は必要なものですが、まだありませんこれを使用するには...

UPD6:

/* read/write buffer sizes */ 
#define IN_BUF_MAX 409600 
#define OUT_BUF_MAX 409600 
/* analogous to xz CLI options: -0 to -9 */ 
#define COMPRESSION_LEVEL 7 

/* boolean setting, analogous to xz CLI option: -e */ 
#define COMPRESSION_EXTREME true 
0123:

あなたもこのようなものが必要

このコードを表示するには、可視範囲内で動作する必要があります。

UPD7:

更新されたコードは、すべてがテストされ、作業、私はliblzmaがない完全にスレッドセーフことを発見した、私は、ファイルリストのマルチスレッドの圧縮を作ってみました。それは非常に頻繁にクラッシュします。

このページ
+1

感謝をデコーダのバイナリを使用してください! –

+0

LZMAを代わりに使用できましたか? –

答えて

7

、あなたはさまざまな言語やいくつかのバイナリのソースコードを提供LZMA SDKを見つける:http://www.7-zip.org/sdk.html

次の2つのソリューションを持っている:

  • が着信を解凍するためにC++ソースコードを使用します
  • を流し、あなたのアプリで外部ツールとして更新と完全なソリューションの多くのための
+0

thx for info、現在私は別の解決策を見つけました。qtのqnetworkmanagerとxz-utilsのliblzmaに基づいてストリームデコンプレッサークラスを作成しました。(xz-utilsのコード例はわかりやすいように見えます) 。 – sss123next

+0

@Patrice Bernassolaどのような提案? – sss123next

+0

投稿したコードに問題がありますか? –