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