今回は再びプログラミングの話
ZIP圧縮ルーチンをDLLなどつかわず自力で書きました。
だからその覚書をのこしておきたい。
以下はvisual studio 2010で行いました。
参考にしたのはこれ
http://hp.vector.co.jp/authors/VA016379/cpplib/zlib.htm
http://www.tnksoft.com/reading/zipfile/arczip.php
まず必要なものとしてZLIB(http://www.zlib.net/)があります。
最初にこれをダウンロード(現在zlib127.zip)します。
次に適当なLIBプロジェクトを作成してその中にzlib127.zipを解凍して
できた中身のルートのファイルを(サブフォルダをの中のもの以外)すべて追加
最後にコンパイルしてLIBをを得ます。このときプリコンパイルヘッダをOFFでしないとエラーになります。
ここではこれをzlib.lib(release),zlibd.lib(debug)とします。
次にこれを使用するアプリを作成します。
適当なDLGベースのアプリを作成、ボタンを一個置きます。
次にzlib.libをこのプロジェクトに追加します。
ファイルをプロジェクトにコピー後
プロジェクトの設定で追加してもいいですが
プロジェクトのファイルに
#ifdef _DEBUG
#pragma comment(lib,"zlibd.lib")
#else
#pragma comment(lib,"zlib.lib")
#endif
と書いたほうが、うざくなくて私はすきです。
次にヘッダー類のコピーですが、
zconf.h
zlib.h
をコピーします。
そのまま使ってもいいわけですが、
http://www.tnksoft.com/reading/zipfile/arczip.php
から拝借した
ziputil.cpp
ziputil.h
を経由して使いました。
結局プロジェクトには
#include "ziputil.h"
とかけばいいです。
さらにZIPのファイル情報を管理するMFCクラスを作成しました
class CZipFileInfo : public CObject
{
public:
CZipFileInfo();
virtual ~CZipFileInfo();
CStringA strpath;
int buflen;
unsigned char *buff;
__time64_t filetime;
};
typedef CTypedPtrArray< CPtrArray, CZipFileInfo* > CZipFileInfoArray;
これでようやく環境が整ったのでコーデイング出来ます。
2関数作成しました
//ZIPファイル検索
void SearchPath(CString strpath,CString strCur,CZipFileInfoArray & ZipInfoArry){
CFileFind FileFind;
BOOL FndEndJug;
//-----------------------------
//検索ファイル名文字列を生成
CString strSearchFile = strpath + _T("\\*.*") ;
//----------------
//検索実行
if(!FileFind.FindFile(strSearchFile))
return;
FndEndJug = TRUE;
while(FndEndJug){
//-------
//検索
FndEndJug = FileFind.FindNextFile();
// "." , ".."を無視
if(FileFind.IsDots())
continue;
//検索結果の判定
if(FileFind.IsDirectory())
{
// サブ・ディレクトリ内を検索するための再帰呼び出し
SearchPath(FileFind.GetFilePath(),strCur,ZipInfoArry);
}
else {
// 検索結果をリスト・ボックスへ出力
CString fullpath=FileFind.GetFilePath();
//TRACE(fullpath+L"\n");
TCHAR RelativePath[MAX_PATH];
PathRelativePathTo(RelativePath,strCur, FILE_ATTRIBUTE_DIRECTORY,fullpath,FILE_ATTRIBUTE_ARCHIVE);
//ファイルデータ取り出し
struct _stat64i32 st0;
_wstat(fullpath,&st0);
FILE * fp0=NULL;
_tfopen_s(&fp0,fullpath,L"rb");
unsigned char * buff=new unsigned char [st0.st_size];
fread(buff,1,st0.st_size,fp0);
fclose(fp0);
CZipFileInfo * pInfo=new CZipFileInfo;
pInfo->strpath=RelativePath;
pInfo->buflen=st0.st_size;
pInfo->buff=buff;
pInfo->filetime=st0.st_mtime;
ZipInfoArry.Add(pInfo);
}
}
}
//圧縮
void CreateZip(CString strpath,CString strZip){
TCHAR chCur[MAX_PATH];
GetCurrentDirectory(MAX_PATH,chCur);
//ファイルをサーチ
CZipFileInfoArray ZipInfoArry;
SearchPath(strpath,chCur,ZipInfoArry);
//ファイル数
int nLen=ZipInfoArry.GetCount();
if(nLen<=0)return;
// ヘッダ情報の作成
ZipHeader * zipheader=new ZipHeader [nLen];
CentralDirHeader * archeader=new CentralDirHeader [nLen];
memset(zipheader, 0, sizeof(ZipHeader) * nLen);
memset(archeader, 0, sizeof(CentralDirHeader) * nLen);
for(int i=0;i<nLen;i++){
TRACE(ZipInfoArry[i]->strpath+"\n");
zipheader[i].signature = ZIPSIG_CENTDIR; // PK0304
zipheader[i].needver = 20; // Deflate圧縮を利用できるVer
zipheader[i].comptype = 8; // Deflate圧縮
// ファイル時刻を取得
__time64_t time64=ZipInfoArry[i]->filetime;
tm nowtm;
_localtime64_s(&nowtm,&time64);
zipheader[i].filedate = GetDosDate(nowtm.tm_year + 1900, nowtm.tm_mon+1, nowtm.tm_mday);
zipheader[i].filetime = GetDosTime(nowtm.tm_hour, nowtm.tm_min, nowtm.tm_sec);
// 展開時に用いるファイル名の設定
zipheader[i].fnamelen = ZipInfoArry[i]->strpath.GetLength();
zipheader[i].filename = ZipInfoArry[i]->strpath.GetBuffer();
// ファイルサイズの設定
zipheader[i].uncompsize = ZipInfoArry[i]->buflen;
archeader[i].signature = ZIPSIG_ARCHIVEFILE; // PK0102
archeader[i].madever = 20;
}
FILE *fp=NULL;
_tfopen_s(&fp,strZip, _T("wb"));
if(fp == NULL){
printf("ファイルが開けませんでした。");
return;
}
// 圧縮関連変数の初期化
const uInt tempsize = 16384*0x50;
Byte * tempbuf=new Byte [tempsize];
uInt compsize;
z_stream zs;
memset(&zs, 0, sizeof(z_stream));
deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
// ファイルの位置を一時的に格納する変数
unsigned int current;
// アーカイブデータの書き込み
InitCRC32();
for(int i=0;i<nLen;i++){
// データの位置を格納
archeader[i].headerpos = ftell(fp);
// zipヘッダの書き込み
WriteZipHeader(fp, &zipheader[i]);
// 現在のファイル位置を一時的に取得
current = ftell(fp);
// zlib構造体をメモリの開放を行わず再初期化する
deflateReset(&zs);
// crc32の初期値は0xFFFFFFFFにする
zipheader[i].crc32 = CRC32_DEFAULT;
// データの圧縮と書き込み
compsize = 0;
zs.avail_in = zipheader[i].uncompsize;
zs.next_in = (Bytef*)ZipInfoArry[i]->buff;
do{
zs.avail_out = tempsize;
zs.next_out = tempbuf;
// CRC32値の更新
//zipheader[i].crc32 = GetCRC32(zs.next_in, zs.avail_in, zipheader[i].crc32);
zipheader[i].crc32 = GetCRC32(ZipInfoArry[i]->buff,ZipInfoArry[i]->buflen);
deflate(&zs, Z_FINISH);
fwrite(tempbuf, 1, tempsize - zs.avail_out, fp);
}while(zs.avail_out == 0);
// 圧縮サイズデータの更新
zipheader[i].compsize = ftell(fp) - current;
current = ftell(fp);
// ファイルデータの書き換え
fseek(fp, archeader[i].headerpos + 14, SEEK_SET);
fwrite(&zipheader[i].crc32, 1, sizeof(unsigned int), fp);
fwrite(&zipheader[i].compsize, 1, sizeof(unsigned int), fp);
fseek(fp, 0, SEEK_END);
// ヘッダ内容のコピー
CopyToCentralDirHeader(&archeader[i], &zipheader[i]);
}
// zlibメモリの開放
deflateEnd(&zs);
// 中央ディレクトリ情報開始位置の格納
current = ftell(fp);
// 中央ディレクトリの書き込み
for(int i = 0; i < nLen; i++){
WriteCentralDirHeader(fp, &archeader[i]);
}
// 終端ヘッダの書き込み
EndCentDirHeader endheader;
memset(&endheader, 0, sizeof(EndCentDirHeader));
endheader.signature = ZIPSIG_ENDCENTDIR; // PK0506
endheader.direntry = nLen;
endheader.diskdirentry = endheader.direntry;
endheader.startpos = current;
endheader.dirsize = ftell(fp) - current;
WriteEndCentDirHeader(fp, &endheader);
fclose(fp);
for(int i=0;i<nLen;i++){
ZipInfoArry[i]->strpath.ReleaseBuffer();
delete [] ZipInfoArry[i]->buff;
delete ZipInfoArry[i];
}
delete [] tempbuf;
delete [] zipheader;
delete [] archeader;
}
で最後にこれを使う部分をボタンの応答関数に書きます。
void CZIPsampleDlg::OnBnClickedZipRun()
{
//workのフルパスを作成
TCHAR cbuff[MAX_PATH];
ZeroMemory(cbuff,sizeof(cbuff));
GetCurrentDirectory(MAX_PATH,cbuff);
lstrcat(cbuff,L"\\work");
CTime tm=CTime::GetTickCount();
CString filename=tm.Format("%Y-%m-%d %H-%M.zip");
CreateZip(cbuff,filename);
}
これでカレントのworkフォルダの中身が現在時刻のファイルに圧縮されます。
ここまで読んでくれた人のためにプロジェクトを添付します。
サンプルプロジェクト
0 件のコメント:
コメントを投稿