RTFデータの圧縮保存とSQLiteでの管理方法

MFC、MDI、VS2022、マルチバイト、Win32 Debug、静的リンク、zlib-1.2.11、SQLite

ZLIBを使用して画像を含むRTFテキストを圧縮し、SQLiteに保存した後、読み出して解凍・復元する方法。

  1. ZLIBのソースファイルをダウンロードし、解凍後VSで静的ライブラリzlibstat.libをコンパイルし、プロジェクトディレクトリに配置します。

  2. プロジェクトにZLIB_WINAPIコンパイルスイッチを追加します。

  3. ZLIB内の2つのヘッダーファイルをプロジェクトディレクトリにコピーします。

  4. 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;
}

タグ: MFC SQLite ZLIB RTF データ圧縮

5月23日 12:59 投稿