Qtからお手軽にZIPファイルを扱えるQuaZIPの紹介

 この投稿は、Qt Advent Calendar 2016の22日目の投稿です。
 毎年、この日を確保しております。理由は、簡単、冬コミの原稿が終わっているはずだから!
 そして、過去2回はCalendarエレメントがらみの記事を投稿していたので、今年もそうしようかなーと思っていたのですが、変更しました。
 QuaZIPについての日本語の情報ってサラッと探した感じだとないのですが、非常にお手軽なので別にって感じなのかもですけれども、あえて書いてみる事にしました。
 むしろ、QuaZIPのライブラリを使うためのqmakeの設定(*.pro)の方がもしかしたら慣れてない人にはありがたい内容かもしれない、と思った次第です。ガンガンに使っている人にとっては、基礎だと思いますが。

 と言うわけで、QtでいわゆるZIPファイルを「簡単」に扱うにはQuaZIPというライブラリが非常に便利です。

  公式サイト:QuaZIP - Qt/C++ wrapper for ZIP/UNZIP package

 このライブラリ自体がQtで作られているわけなので、使わなくてもZIPファイルを扱えますが、折角のライブラリを使わない手はありません。プロジェクトの事情でも無い限り使いたいところです。
 というわけで、QuaZIP自体のビルドからお手軽な使用方法までを紹介します。
 ちなみに、LGPL 2.1です。


/// 環境 ///
 今回紹介するに当たっての環境は以下の通りです。
 ・QuaZIP 0.7.2
 ・Windows 10(64bit)
 ・Qt 5.7.0
 ・Visual Studio 2015

 試しては居ませんが、Qt4系にも対応しているようです。


/// 準備 ///
 公式サイトからソースコードのアーカイブを入手します。
 それをお好きな作業フォルダに解凍します。
 以下のフォルダ構成で作業を進めることを想定しています。

 作業フォルダ:C:\QtProjects
 解凍フォルダ:C:\QtProjects\quazip-0.7.2


/// ビルド方法 ///
 ビルドはビルド用のフォルダを作るシャドウビルドで行います。
 32bit版もビルドする前提でフォルダ名などをつけていますので、適宜読み替えてください。
 qmakeするときにzlib.h用にインクルードパスを追加する必要があります。この辺りは公式にも環境に合わせて適宜やってねって書いてありますけども。
 PREFIXに指定するパスは、カレントからではなくquazip.proからの相対パスです。

 >"c:\Program Files\Microsoft Visual Studio 14.0\vc\vcvarsall.bat" x86_amd64
 >PATH=%PATH%;C:\Qt\5.7\msvc2015_64\bin

 >mkdir build-quazip-x64
 >cd build-quazip-x64

 >qmake -r ..\quazip-0.7.2\quazip\quazip.pro "PREFIX=..\..\quazip\x64" "INCLUDEPATH+=$$[QT_INSTALL_PREFIX]/include/QtZlib"
 >nmake all
 >nmake release-install debug-install

 以下のファイルができていればOKです。
 .\quazip\x64\lib\quazip.dll
 .\quazip\x64\lib\quazip.lib
 .\quazip\x64\lib\quazipd.dll
 .\quazip\x64\lib\quazipd.lib
 .\quazip\x64\lib\quazipd.pdb
 .\quazip\x64\include\quazip\* h

 Macの場合は、INCLUDEPATHの代わりに「LIBS+=-lz」を追加します。


/// アプリケーションに組み込み ///
 適当なアプリケーションプロジェクトを作成します。
 今回は特にUIどうこう気にしないので、ボタンを押したらテストコードが実行できる感じでいきますので、Qtウィジェットのプロジェクトが簡単で良いと思います。
 プロジェクト名「QuazipExample」で進めます。
 まず、ライブラリを追加する設定を記述するファイルを作成します。

quazip.pri
# for quazip
QUAZIP_DIR = $${PWD}/../quazip
win32:{
    TARGET_PLATROM = $$(PLATFORM)
    equals(TARGET_PLATROM, "X64"):{
        # 64bit
        QUAZIP_DIR = $$QUAZIP_DIR/x64
    }else{
        # 32bit
        QUAZIP_DIR = $$QUAZIP_DIR/x86
    }
}
INCLUDEPATH += $$QUAZIP_DIR/include
CONFIG(debug,debug|release){
    win32: LIBS += $$QUAZIP_DIR/lib/quazipd.lib
}else{
    win32: LIBS += $$QUAZIP_DIR/lib/quazip.lib
}
unix: LIBS += -L$$QUAZIP_DIR/lib/ -lquazip

# for zlib
win32: INCLUDEPATH += $$[QT_INSTALL_PREFIX]/include/QtZlib


# copy library file
win32:{
    CONFIG(debug,debug|release){
        QUAZIP_LIB_PATH=$$QUAZIP_DIR/lib/quazipd.dll
        APP_BUILD_DIR=$$shadowed($${PWD})/debug
    }else{
        win32:QUAZIP_LIB_PATH=$$QUAZIP_DIR/lib/quazip.dll
        APP_BUILD_DIR=$$shadowed($${PWD})/release
    }
    QUAZIP_LIB_PATH ~= s|/|\|gi
    APP_BUILD_DIR ~= s|/|\|gi
}else{
    QUAZIP_LIB_PATH=$$QUAZIP_DIR/lib/libquazip.so.*
    APP_BUILD_DIR=$$shadowed($${PWD})
}
copyquazip.commands = $(COPY_DIR) $$QUAZIP_LIB_PATH $$APP_BUILD_DIR
QMAKE_EXTRA_TARGETS += copyquazip
 後半でカスタムターゲットとして「copyquazip」を作成します。これをプロジェクトの設定のビルドステップに追加すると自動でdllがコピーされるようになります。
 カスタムターゲットのコマンドの「$(COPY_DIR)」はMakefileにこのまま出力されます。そして、Makefileの中でWindowsだとxcopyを使うように設定されていますので、proファイルでプラットフォームごとの設定を書かなくてもすみます。
 この処理は、実はMacは必要ないです。macdeployqtを実行すると勝手にコピーしてくれるのでズルいですw

 続いて、上記のファイルをプロジェクトファイルから読み込まれるようにします。
QuazipExample.pro
    include(quazip.pri)
 プロジェクトの設定は、「ビルドステップ」→「ビルドステップを追加」で「Make」を選択し、「Makeの引数」に「copyquazip」と入力します。
 つまり、
 >make copyquazip
 をやってくれるようにするわけです。

 Qt Creatorからビルドするときや、Qtをインストールしたときに作られるコマンドプロンプトのショートカット「Qt 5.7 64-bit for Desktop (MSVC 2015)」などを使用すると64bit環境のときだけ環境変数として「PLATFORM=X64」が定義されています。これを利用して使用するライブラリのパスを切り替えられるようにしています。
 このファイルの設定について詳しいことを知りたい方は、緑野翁さんの著書「qmake入門」を参照してください。BOOK☆WALKERか今冬のコミケC91(2016/12/29 ビックサイト)のスペース「西み32b」で入手できます。
  BOOK☆WALKER:qmake入門


/// お手軽な圧縮と解凍 ///
 QuaZIPには、簡単お手軽機能をまとめた「JlCompress」クラスがあります。
 特定のファイルを圧縮したり、解凍したりがお手軽ポンです。
 紹介する関数は全てstaticです。

 リファレンス:JlCompress Class Reference

 正直、ここを見れば説明不要って感じですが、一応めげずに紹介します。

bool JlCompress::compressFile(QString fileCompressed,
                              QString file)
fileに指定されているファイルを圧縮します。

bool JlCompress::compressFiles(QString fileCompressed,
                               QStringList files)
filesに登録してあるファイルを圧縮します。
色々な場所に散らばったファイルを選んで圧縮する場合などに使えそうですね。


bool JlCompress::compressDir(QString fileCompressed,
                             QString dir = QString(), 
                             bool recursive = true)
dirに指定したフォルダの中身を圧縮します。
recursiveをfalseにするとサブフォルダの中は無視されます。もちろんフォルダ自体も無視されます。
dirを省略すると、カレントフォルダの中身を圧縮します。
隠しファイルは無視されます。


bool JlCompress::compressDir(QString fileCompressed,
                             QString dir, bool recursive,
                             QDir::Filters  filters)
これもフォルダごと圧縮します。
基本は同じですが、filtersが指定できるので隠しファイルを含められます。
QDir::Hiddenを指定すると隠しも含めて保存されます。
QDir::Filesなどを指定すると空っぽの結果になります。なぜだろう......。


 ここまでが圧縮系(compressHoge)の関数です。これらは、圧縮後のパスに既存のファイルを指定すると上書きします。追加ではありません。
 フォルダは無圧縮でファイルは必ず圧縮されます。一般のアーカイブソフトなどですと何らかの条件で圧縮したりしなかったりしているみたいですが、QuaZIPでは一律圧縮です。


QStringList JlCompress::getFileList(QString fileCompressed)
圧縮されているファイルの一覧を取得します。
この後紹介するファイルを個別で解凍するときに必要になります。

QString JlCompress::extractFile(QString fileCompressed,
                                QString fileName,
                                QString fileDest = QString() 
                                )
fileNameで指定したファイルのみを解凍します。
ZIPファイルの中にフォルダがあれば、それも含めて指定します。getFileListで取得したリストの中から選べばOKです。
解凍先を省略するとカレントフォルダに、解凍されます。
指定するとフォルダ構成を無視して指定したファイル名(パス)で解凍されます。

QStringList JlCompress::extractFiles(QString fileCompressed,
                                     QStringList files,
                                     QString dir = QString() 
                                     )
filesに指定したファイルをZIPファイルの中から解凍します。
dirに指定したフォルダに解凍します。指定したフォルダが存在しないと自動で作成されます。
dirを省略するとカレントフォルダに解凍されます。

QStringList JlCompress::extractDir(QString fileCompressed,
                                   QString dir = QString() 
                                   )
dirに指定したフォルダに解凍します。
無ければ作成されます。

 以上です。簡単ですね。
 今回紹介した関数はすべて圧縮ファイルのパスを文字列で与えていますが、中にはQIODeviceを使用して解凍できる関数もあります。
 何となくご理解いただいていると思いますが、今回紹介したクラスはお手軽なのですが、できることが大味です。個別のファイル単位で圧縮したり無圧縮でパッキングだけしたり、と処理を変えたりするには別のクラスを使用しなければなりません。圧縮したけどかえってサイズが大きくなったとかあるので、理想を言えばいろいろ頑張る必要があります。結局のところケースバイケースなのでご要求に合わせて考えてみてください。なので、細かい話はまた別の機会にしたいと思います。  結局、ライブラリを紹介したいのか、*.proファイルの設定を紹介したいのか謎な感じになりましたが、参考になれば何よりです。