使用zlib壓縮IStream流

使用zlib壓縮IStream流 by sdragon

比較完善的一個壓縮流類,已經能夠使用了。歡迎提出意見,具體使用方法,看看代碼吧。

頭文件:

#ifndef CompressStreamH
#define CompressStreamH
//---------------------------------------------------------------------------

#include <windows.h>
#include <objbase.h>        //IStorage, IStream
#include <atl/atlbase.h>    //CComPtr, CComBSTR

namespace zlib
{
#include <zlib/zlib.h>
}using namespace zlib;
#pragma link "zlib.lib"

#include <cstdio>
#include <vector>
#include <memory>
#include <cassert>
#include <stdint.h>
using namespace std;

#pragma pack(push, 1)

struct Z_DATAINFO
{
    uint16_t version;
    int16_t  level;
    uint32_t size;
    uint32_t src_size;
    uint32_t crc32;
};

#ifndef Z_CRC32_VERIFY_ERROR
#define Z_CRC32_VERIFY_ERROR = -128
#endif

#pragma pack(pop)

class CCompressStream
{
private:
    const static DWORD STATE_NONE    = 0;
    const static DWORD STATE_DEFLATE = 1;
    const static DWORD STATE_INFLATE = 2;
    const static DWORD BUFFER_SIZE   = 1024*8;
public:
    CCompressStream();
    CCompressStream(CComPtr<IStream>& lpStream);
    ~CCompressStream();
    int BeginDeflate(int level = Z_DEFAULT_COMPRESSION);
    int EndDeflate();
    int BeginInflate();
    int EndInflate();
    int Write(BYTE *lpData, DWORD dwSize, LPDWORD pWritten);
    int Read(BYTE *lpData, DWORD dwSize, LPDWORD pRead);
    int SaveToFile(const char* fname);
    int LoadFromFile(const char* fname);
    IStream** operator&(){ return &m_lpStream; }
private:
    CComPtr<IStream> m_lpStream;
    DWORD            m_dwState;
    z_stream         m_zstm;
    vector<char>     m_buffer;
    Z_DATAINFO       m_info;
};

//---------------------------------------------------------------------------
#endif 

源文件:

//---------------------------------------------------------------------------


#pragma hdrstop

#include "CompressStream.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

CCompressStream::CCompressStream()
    :m_dwState(STATE_NONE)
{
}

CCompressStream::CCompressStream(CComPtr<IStream>& lpStream)
    :m_lpStream(lpStream), m_dwState(STATE_NONE)
{
}

CCompressStream::~CCompressStream()
{
    assert(m_dwState == STATE_NONE);
}

int CCompressStream::BeginDeflate(int level)
{
    assert(m_dwState == STATE_NONE);
   
    int result       = 0;
    LARGE_INTEGER li = {sizeof(Z_DATAINFO)};

    memset(&m_zstm, 0, sizeof(z_stream));
    result = deflateInit(&m_zstm, level);
    if (result == Z_OK)
    {
        m_dwState = STATE_DEFLATE;
        m_lpStream->Seek(li, STREAM_SEEK_SET, NULL);
        m_buffer.reserve(BUFFER_SIZE);
        memset(&m_info, 0, sizeof(Z_DATAINFO));
        m_info.version = ZLIB_VERNUM;
        m_info.level   = level;
        m_info.crc32   = zlib::crc32(0, NULL, 0);
    }
    return result;
}

int CCompressStream::EndDeflate()
{
    assert(m_dwState == STATE_DEFLATE);

    //寫入剩餘緩衝區的數據
    this->Write(NULL, 0, NULL);

    //寫入Z_DATAINFO信息
    LARGE_INTEGER li = {0};
    m_lpStream->Seek(li, STREAM_SEEK_SET, NULL);
    m_lpStream->Write(&m_info, sizeof(m_info), NULL);

    //清理環境
    m_dwState = STATE_NONE;
    m_buffer.swap(vector<char>());
    return deflateEnd(&m_zstm);;
}

int CCompressStream::Write(BYTE *lpData, DWORD dwSize, LPDWORD pWritten)
{
    assert(m_dwState == STATE_DEFLATE);

    //Z_NO_FLUSH 表示可能還有數據,Z_FINISH 表示不再有數據了,處理完
    //數據後就可以結束了。這裏如果(dwSize == 0),表示沒有數據了,處理
    //完緩衝內的數據後結束。
    int    flush   = dwSize ? Z_NO_FLUSH : Z_FINISH;
    size_t szdata  = 0;    //compress data size
    DWORD  written = 0;    //for stream write
    int    result  = 0;

    //計算crc32校驗值
    if(dwSize != 0)m_info.crc32 = zlib::crc32(m_info.crc32, lpData, dwSize);
    //統計源數據長度
    m_info.src_size += dwSize;

    m_zstm.avail_in = dwSize;
    m_zstm.next_in  = lpData;

    do
    {
        m_zstm.avail_out = BUFFER_SIZE;
        m_zstm.next_out  = &m_buffer.front();

        result = deflate(&m_zstm, flush);
        assert(result != Z_STREAM_ERROR);
        szdata = BUFFER_SIZE - m_zstm.avail_out;
        m_info.size += szdata;  //統計壓縮數據長度

        m_lpStream->Write(&m_buffer.front(), szdata, &written);
        if(pWritten)*pWritten = written;
        if(written != szdata)//檢測可能出現的數據寫入錯誤
        {
            return Z_ERRNO;
        }

    }while(m_zstm.avail_out == 0);  //avail_out == 0 表示可能
                                    //還有數據沒有得到壓縮
    assert(m_zstm.avail_in == 0);   // all input will be used
    return result;
}

int CCompressStream::BeginInflate()
{
    assert(m_dwState == STATE_NONE);

    int result       = 0;
    LARGE_INTEGER li = {0};

    memset(&m_zstm, 0, sizeof(z_stream));
    result = inflateInit(&m_zstm);
    if (result == Z_OK)
    {
        DWORD dwRead = 0;
        m_dwState = STATE_INFLATE;
        m_lpStream->Seek(li, STREAM_SEEK_SET, NULL);
        m_lpStream->Read(&m_info, sizeof(m_info), &dwRead);
        assert(dwRead != 0);
        m_buffer.reserve(BUFFER_SIZE);
    }
    return result;
}

int CCompressStream::EndInflate()
{
    assert(m_dwState == STATE_INFLATE);
   
    m_dwState = STATE_NONE;
    m_buffer.swap(vector<char>());
    return inflateEnd(&m_zstm);;
}

int CCompressStream::Read(BYTE *lpData, DWORD dwSize, LPDWORD pRead)
{
    assert(m_dwState == STATE_INFLATE);
    assert(dwSize != 0);

    int result = 0;

    //avail_in == 0 表示當前數據已經解壓完畢,可以讀入新的壓縮數據
    if(m_zstm.avail_in == 0)
    {
        DWORD dwRead = 0;
        m_lpStream->Read(&m_buffer.front(), BUFFER_SIZE, &dwRead);
        if(dwRead == 0)
        {
            return Z_DATA_ERROR;
        }
        m_zstm.avail_in = dwRead;
        m_zstm.next_in  = &m_buffer.front();
    }

    m_zstm.avail_out = dwSize;
    m_zstm.next_out  = lpData;
    result = inflate(&m_zstm, Z_NO_FLUSH);
    *pRead = dwSize - m_zstm.avail_out;

    //Z_STREAM_END 表示流中的壓縮數據已經完全讀取,解壓後就可以關閉了
    //所以這裏返回Z_OK,進行最後一次解壓
    return result == Z_STREAM_END ? Z_OK : result;
}

int CCompressStream::SaveToFile(const char* fname)
{
    char   buf[BUFFER_SIZE] = {0};
    DWORD  size   = 0;
    int    result = 0;
    FILE*  f = fopen(fname, "wb");
    if(!f)return Z_ERRNO;
    result = this->BeginInflate();
    if(result == Z_OK)
    {
        while(this->Read(buf, BUFFER_SIZE, &size) == Z_OK)
        {
            if((fwrite(buf, 1, size, f) != size) || ferror(f))
            {
                fclose(f);
                this->EndInflate();
                return Z_ERRNO;
            }
        }
        fclose(f);
        result = this->EndInflate();
    }
    return result;
}

int CCompressStream::LoadFromFile(const char* fname)
{
    char   buf[BUFFER_SIZE] = {0};
    size_t size   = 0;
    int    result = 0;
    FILE *f = fopen(fname, "rb");
    if(!f)return Z_ERRNO;
    result = this->BeginDeflate();
    if(result != Z_OK)return result;
    do
    {
        size = fread(buf, 1, BUFFER_SIZE, f);
        if (ferror(f))goto LOCAL_ERROR;
        result = this->Write(buf, size, NULL);
        if(result != Z_OK)goto LOCAL_ERROR;
    }while(!feof(f));

    fclose(f);
    return this->EndDeflate();
LOCAL_ERROR:
    fclose(f);
    this->EndDeflate();
    return Z_ERRNO;
}

測試代碼:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
    _di_IStorage pstg;
    CCompressStream pstm;

 HRESULT hr = StgCreateDocfile(
  L"DocFile.stg",
  STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DIRECT,
  0,
  &pstg);

    if(SUCCEEDED(hr))
    {
  hr = pstg->CreateStream(
    L"MainElement",
    STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE|STGM_DIRECT,
    0, 0,
                &pstm);
        if(SUCCEEDED(hr))
        {
            print(pstm.LoadFromFile("未命名.bmp"));  //將文件導入流
            print(pstm.SaveToFile("未命名2.bmp"));     //將文件導出到文件
        }
    }

發佈了40 篇原創文章 · 獲贊 8 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章