MFC、MDI、VS2022、マルチバイト、Win32 Debug、静的リンク、zlib-1.2.11、SQLite
ZLIBを使用して画像を含むRTFテキストを圧縮し、SQLiteに保存した後、読み出して解凍・復元する方法。
-
ZLIBのソースファイルをダウンロードし、解凍後VSで静的ライブラリzlibstat.libをコンパイルし、プロジェクトディレクトリに配置します。
-
プロジェクトにZLIB_WINAPIコンパイルスイッチを追加します。
-
ZLIB内の2つのヘッダーファイルをプロジェクトディレクトリにコピーします。
-
Web上のサンプルコードを参考に、必要な変更を加えてテストプログラムに実装します。圧縮と解凍の両方が正常に動作します。 デフォルトの圧縮率を使用し、バイト数は8,084,414バイトから2,615,875バイトに圧縮されました。
以下に必要な設定を示します:
プログラムの実行画面:
主要プログラム:
CRtfSQLite.h
#pragma once
#include "sqlite3.h"
class CRtfSQLite
{
public:
CRtfSQLite();
virtual ~CRtfSQLite();
public:
sqlite3* m_database;
char** m_queryResult;
BOOL m_isOpen;
public:
BOOL OpenDatabase(char* filename);
void CloseDatabase();
BOOL ExecuteQuery(char* sql, int &rowCount, int &columnCount);
BOOL ExecuteSqlStatement(char* sql);
static int SqlCallback(void* NotUsed, int argc, char** argv, char** azColName);
int ReadBinaryData(char* sql, void* data);
BOOL SaveBinaryData(char* sql, void* data, int length);
int ReadCompressedRtf(char* sql, CString &content);
int ReadCompressedRtf(char* sql, void* &data);
BOOL UpdateBinaryData(char* sql, void* data, int length);
BOOL InsertBinaryData(char* sql, int columnIndex, void* data, int length);
// 圧縮関連のメソッド
BOOL UpdateCompressedData(char* sql, void* data, int length);
int ReadDecompressedRtf(char* sql, CString& content);
public:
};
CRtfSQLite.cpp
以前のテストコードがいくつか残っていますが、圧縮保存と解凍復元の機能は最後の2つの関数に実装されています。
解凍には事前にバッファを確保する必要がありますが、そのサイズは解凍前に分かりません。 この問題を解決するための方法として、圧縮前のデータサイズを保存するフィールドを追加する方法があります。
#include "StdAfx.h"
#include "CRtfSQLite.h"
// ZLIB関連のインクルード
#include "zlib.h"
#include "zconf.h"
#pragma comment(lib,"zlibstat.lib")
#include "RE2.h" // デバッグ出力用
CRtfSQLite::CRtfSQLite()
{
// メンバの初期化
m_queryResult = NULL;
m_database = NULL;
m_isOpen = FALSE;
}
CRtfSQLite::~CRtfSQLite()
{
if (NULL != m_queryResult)
{
sqlite3_free_table(m_queryResult);
m_queryResult = NULL;
}
if (NULL != m_database)
{
sqlite3_close(m_database);
m_database = NULL;
}
m_isOpen = FALSE;
}
BOOL CRtfSQLite::OpenDatabase(char* filename)
{
int result;
if (NULL != m_database)
{
sqlite3_close(m_database);
m_database = NULL;
m_isOpen = FALSE;
}
result = sqlite3_open(filename, &m_database);
if (result)
{
sqlite3_close(m_database);
return FALSE;
}
m_isOpen = TRUE;
return TRUE;
}
void CRtfSQLite::CloseDatabase()
{
if (NULL != m_database)
{
sqlite3_close(m_database);
m_database = NULL;
}
m_isOpen = FALSE;
}
// SQLクエリの実行
BOOL CRtfSQLite::ExecuteQuery(char* sql, int& rowCount, int& columnCount)
{
char* errorMessage;
int result;
if (NULL != m_queryResult)
{
sqlite3_free_table(m_queryResult);
m_queryResult = NULL;
}
result = sqlite3_get_table(m_database, sql, &m_queryResult, &rowCount, &columnCount, &errorMessage);
if (result != SQLITE_OK)
{
if (NULL != errorMessage)
{
sqlite3_free(errorMessage);
return FALSE;
}
}
return TRUE;
}
// SQL実行のコールバック関数
int CRtfSQLite::SqlCallback(void* NotUsed, int argc, char** argv, char** azColName)
{
int i;
for (i = 0; i < argc; i++)
{
// コールバック処理
}
return 0;
}
// SQLステートメントの実行
BOOL CRtfSQLite::ExecuteSqlStatement(char* sql)
{
char* errorMessage;
int result;
result = sqlite3_exec(m_database, sql, SqlCallback, 0, &errorMessage);
if (result != SQLITE_OK)
{
sqlite3_free(errorMessage);
return FALSE;
}
return TRUE;
}
// バイナリデータの読み出し
int CRtfSQLite::ReadBinaryData(char* sql, void* data)
{
int i;
CString resultStr;
sqlite3_stmt* statement = 0;
const char* error = 0;
int result;
int length;
result = sqlite3_prepare(m_database, sql, strlen(sql), &statement, &error);
if (result != SQLITE_OK)
{
return 0;
}
while (1)
{
result = sqlite3_step(statement);
if (result != SQLITE_ROW) break;
data = (void*)sqlite3_column_blob(statement, 1);
length = sqlite3_column_bytes(statement, 1);
}
sqlite3_finalize(statement);
return length;
}
int CRtfSQLite::ReadCompressedRtf(char* sql, CString &content)
{
sqlite3_stmt* statement = 0;
const char* error = 0;
int result;
int length;
void* data = NULL;
result = sqlite3_prepare(m_database, sql, strlen(sql), &statement, &error);
if (result != SQLITE_OK)
{
return 0;
}
while (1)
{
result = sqlite3_step(statement);
if (result != SQLITE_ROW)
break;
data = (void*)sqlite3_column_blob(statement, 0);
length = sqlite3_column_bytes(statement, 0);
content += (char*)data;
}
sqlite3_finalize(statement);
return length;
}
int CRtfSQLite::ReadCompressedRtf(char* sql, void* &data)
{
sqlite3_stmt* statement = 0;
const char* error = 0;
int result;
int length;
result = sqlite3_prepare(m_database, sql, strlen(sql), &statement, &error);
if (result != SQLITE_OK)
{
return 0;
}
while (1)
{
result = sqlite3_step(statement);
if (result != SQLITE_ROW)
break;
data = (void*)sqlite3_column_blob(statement, 0);
length = sqlite3_column_bytes(statement, 0);
}
sqlite3_finalize(statement);
return length;
}
BOOL CRtfSQLite::SaveBinaryData(char* sql, void* data, int length)
{
sqlite3_stmt* statement = 0;
int result;
const char* error = 0;
result = sqlite3_prepare(m_database, sql, strlen(sql), &statement, &error);
if (result != SQLITE_OK)
{
return FALSE;
}
result = sqlite3_bind_blob(statement, 1, (const void*)data, length, SQLITE_STATIC);
result = sqlite3_step(statement);
sqlite3_finalize(statement);
return TRUE;
}
BOOL CRtfSQLite::InsertBinaryData(char* sql, int columnIndex, void* data, int length)
{
sqlite3_stmt* statement = 0;
int result;
const char* error = 0;
result = sqlite3_prepare(m_database, sql, strlen(sql), &statement, &error);
if (result != SQLITE_OK)
{
return FALSE;
}
result = sqlite3_bind_blob(statement, columnIndex, (const void*)data, length, SQLITE_STATIC);
result = sqlite3_step(statement);
sqlite3_finalize(statement);
return TRUE;
}
// バイナリデータの更新
BOOL CRtfSQLite::UpdateBinaryData(char* sql, void* data, int length)
{
sqlite3_stmt* statement = 0;
int result;
const char* error = 0;
result = sqlite3_prepare(m_database, sql, strlen(sql), &statement, &error);
if (result != SQLITE_OK)
{
return FALSE;
}
result = sqlite3_bind_blob(statement, 1, (const void*)data, length, SQLITE_STATIC);
result = sqlite3_step(statement);
sqlite3_finalize(statement);
return TRUE;
}
// 圧縮データの更新
BOOL CRtfSQLite::UpdateCompressedData(char* sql, void* data, int length)
{
sqlite3_stmt* statement = 0;
int result;
const char* error = 0;
result = sqlite3_prepare(m_database, sql, strlen(sql), &statement, &error);
if (result != SQLITE_OK)
{
return FALSE;
}
Bytef* compressedBuffer;
Bytef* sourceBuffer;
compressedBuffer = new Bytef[length];
sourceBuffer = (Bytef*)data;
uLongf compressedLength;
int sourceLength;
sourceLength = length;
int compressionResult;
compressionResult = compress(compressedBuffer, &compressedLength, sourceBuffer, sourceLength);
PRINT("圧縮: 結果=%d 元サイズ=%d, 圧縮後サイズ=%d", compressionResult, sourceLength, compressedLength);
result = sqlite3_bind_blob(statement, 1, (const void*)compressedBuffer, (int)compressedLength, SQLITE_STATIC);
result = sqlite3_step(statement);
sqlite3_finalize(statement);
delete compressedBuffer;
return TRUE;
}
int CRtfSQLite::ReadDecompressedRtf(char* sql, CString& content)
{
sqlite3_stmt* statement = 0;
const char* error = 0;
int result;
int length;
void* compressedData = NULL;
Bytef* decompressedBuffer;
Bytef* compressedBuffer;
uLongf decompressedLength;
int compressedLength;
int decompressionResult;
result = sqlite3_prepare(m_database, sql, strlen(sql), &statement, &error);
if (result != SQLITE_OK)
{
return 0;
}
while (1)
{
result = sqlite3_step(statement);
if (result != SQLITE_ROW)
break;
compressedData = (void*)sqlite3_column_blob(statement, 0);
length = sqlite3_column_bytes(statement, 0);
compressedBuffer = (Bytef*)compressedData;
compressedLength = length;
decompressedLength = 1024*1024*10; // 10MB - このサイズをどう決定するか?
decompressedBuffer = new Bytef[decompressedLength];
decompressionResult = uncompress(decompressedBuffer, &decompressedLength, compressedBuffer, compressedLength);
PRINT("解凍: 結果=%d 圧縮サイズ=%d, 解凍後サイズ=%d", decompressionResult, compressedLength, decompressedLength);
content += (char*)decompressedBuffer;
delete decompressedBuffer;
}
sqlite3_finalize(statement);
return length;
}