使用zlib压缩IStream流

来源:互联网 发布:c4.5决策树算法python 编辑:程序博客网 时间:2024/05/22 09:47

使用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"));     //将文件导出到文件
        }
    }

原创粉丝点击