1. 程式人生 > >使用zlib解壓.apk/.zip檔案(Windows&Ubuntu)

使用zlib解壓.apk/.zip檔案(Windows&Ubuntu)

前言

前面講過,解壓出apk檔案的內容是進行apk分析的第一步,而.apk檔案其實就是.zip檔案。也就是說首先要實現zip檔案的解壓縮。本文將分別介紹在Windows和Ubuntu下如何使用zlib這一開源庫對zip檔案進行解壓。

Git

ZLIB

zlib is designed to be a free, general-purpose, legally unencumbered – that is, not covered by any patents – lossless data-compression library for use on virtually any computer hardware and operating system. The zlib data format is itself portable across platforms.

zlib是一個C語言編寫的、開源的、幾乎在所有的計算機和作業系統上都使用的資料壓縮庫,更多的介紹請檢視zlib官方網站;
簡而言之,zlib其實是一種資料壓縮演算法,zip檔案壓縮只是這種演算法的應用之一。
關於zlib壓縮演算法參考這篇文章:http://blog.jobbole.com/76676/
在zlib的原始碼中,有一個\contrib\minizip資料夾,這個資料夾中封裝了zlib演算法,使得其可以用來解壓和壓縮zip格式的檔案。

下載ZLIB

zlib目前的版本是1.2.8,要下載的內容包括原始碼和編譯好的dll檔案(也可以自己使用原始碼編譯)。
zlib source code:

http://zlib.net/zlib-1.2.8.tar.gz
zlib compiled DLL:http://zlib.net/zlib128-dll.zip
下載完成後將這兩個檔案解壓。

zlib演算法和zip檔案

zlib演算法

網上有很多關於zlib的文章描述的都是如何使用zlib進行壓縮和解壓位元組流,也就是使用

compress (Bytef *dest,   uLongf *destLen, const Bytef *source, uLong sourceLen)

來對位元組流進行壓縮,使用

 uncompress (Bytef *dest,   uLongf *destLen
,const Bytef *source, uLong sourceLen)

來對位元組流進行解壓縮。
這兩個函式的操作物件就是讀入記憶體的位元組流。下面給出一個使用這兩個函式的例子(暫時不用關心如何正確配置zlib庫使得程式順利編譯)

#include <iostream>
#include <zlib.h>
#include <cstring>
using namespace std;
int main()
{
    char str[11] = "HelloWorld";
    Byte *Source ;
    Byte *Destination;
    unsigned long DestLen ;
    unsigned long SourLen = strlen(str)+1;
    DestLen = compressBound(SourLen);
    Source = new Byte[SourLen];
    Destination = new Byte[DestLen];
    Source = (Byte *)str;
    int err = compress(Destination, &DestLen, Source, SourLen);
    if (err == 0)
    {
        cout <<"壓縮完成"<< endl;
        cout << "原字串:" << Source << endl;
        cout << "壓縮後字串:" << Destination << endl;
        cout << "壓縮後字串長度:" << DestLen << endl;
    }
    else
    {
        cout << "壓縮失敗" << endl;
    }
    Byte *uncompressSource;
    Byte *uncompressDest;
    unsigned long uncompressSourceLen = DestLen;
    unsigned long uncompressDestLen = SourLen;
    uncompressSource = Destination;
    uncompressDest = new Byte[uncompressDestLen];
    err = uncompress(uncompressDest, &uncompressDestLen, uncompressSource, uncompressSourceLen);
    if (err == 0)
    {
        cout << "解壓完成" << endl;
        cout << "解壓前字串:" << uncompressSource << endl;
        cout << "解壓後字串:" << uncompressDest << endl;
        cout << "解壓後後字串長度:" << uncompressDestLen << endl;
    }
    else
    {
        cout << "解壓失敗" << endl;
    }
    system("pause");
    return 0;
}

執行結果:
這裡寫圖片描述
至於為什麼壓縮後的位元組長度比壓縮前還長,那是因為我們壓縮的位元組不夠長(11個Byte),如果有足夠長的位元組,那麼壓縮效果就可以看出來了。
當然,你也可以把“HelloWorld”這個字串替換成從外部檔案中讀取的一段位元組流。
但是這個結果並不是我們想要的,我們要做的事解壓zip檔案。

zip檔案

前面提到,zip檔案壓縮和解壓只是對zlib演算法的一種應用。事實上,在前面的對位元組流的壓縮完成後,如果直接將壓縮後的資料寫入到一個.zip檔案中,使用一般的zip解壓縮軟體是不能開啟的。因為zip檔案有一定的檔案結構。關於zip檔案的結構網上有很多文章,就不再贅述了。
要使用zlib庫對zip檔案進行壓縮和解壓,就需要我們在zlib庫的基礎上再做額外的工作,使得其符合zip檔案結構。感謝zlib的開發人員,這個工作他們已經做好了。
在zlib的原始碼中,找到\contrib\minizip資料夾,這個資料夾中有兩個標頭檔案zip.h和unzip.h,這就是對zlib庫的封裝。還有兩個例子minizip.c和miniunz.c,描述了壓縮和解壓zip檔案的基本操作。

在VS2013中實現解壓zip檔案

配置

  1. 下載前面zlib的原始碼和編譯好的dll檔案,解壓。
  2. 使用vs2013新建一個C++空專案
  3. 將zlib原始碼中的minizip資料夾中的
    • ioapi.h
    • ioapi.c
    • unzip.h
    • unzip.c
      複製到專案目錄中:
      這裡寫圖片描述
  4. 將zlib的dll檔案中/lib資料夾的zdll.lib檔案複製到專案目錄中
  5. 在VS2013中新建一個原始檔
  6. 將ioapi.c和unzip.c新增到專案中
    這裡寫圖片描述
  7. 開啟專案屬性
    這裡寫圖片描述
  8. 將配置屬性->連結器->常規->附加庫目錄修改為剛才新增zdll.lib檔案所在的目錄
    這裡寫圖片描述
  9. 在配置屬性->連結器->輸入->附加依賴項中,新增zdll.lib
    這裡寫圖片描述
  10. 將zlib的DLL檔案中zlib1.dll複製到專案除錯生成的.exe檔案所在的資料夾。

程式碼

標頭檔案

#include <iostream>
#include <zlib.h>
#include "unzip.h"
#include <cstring>
#include <fstream>
#include <direct.h>
using namespace std;

direct.h標頭檔案用來建立目錄
C++建立目錄的操作在Windows和Unix下是不同的。
在Windows下,要

#include<direct.h>

使用

int _mkdir(_In_z_ const char * _Path)

函式。
在Unix下,要

#include<sys/stat.h>
#include<sys/types.h>

使用

int mkdir(const char *pathname, mode_t mode)

函式。
參考這兩篇文章:

API分析

涉及到的minizip庫中的解壓縮函式有:

unzFile unzOpen64(const char *path);
int unzClose(unzFile file);
int unzGetGlobalInfo64(unzFile file, unz_global_info *pglobal_info);
int unzGoToNextFile(unzFile file);
int unzGetCurrentFileInfo64(unzFile file, unz_file_info *pfile_info, char *szFileName, uLong fileNameBufferSize, void *extraField, uLong extraFieldBufferSize, char *szComment, uLong commentBufferSize);
int unzOpenCurrentFile(unzFile file);
int unzCloseCurrentFile(unzFile file);
int unzReadCurrentFile(unzFile file, voidp buf, unsigned len);

一個解壓縮過程包括:
1. 使用unzOpen64函式開啟一個zip檔案;
2. 使用unzGetGlobalInfo64函式讀取zip檔案的全域性資訊,儲存在一個unz_global_info64型別的結構體中;
3. 使用unz_global_info64結構體中的number_entry變數迴圈讀取zip檔案中的檔案,並進行讀取、解壓、寫入、儲存等操作
4. 在unzFile結構中,有一個指標指向當前檔案,預設指在第一個檔案。操作完一個檔案後,使用unzCloseCurrentFile函式關閉這個檔案,再使用unzGoToNextFile函式使得指標指在下一個檔案上,再對該檔案進行操作;
5. 解壓完整個zip檔案後,使用unzClose函式釋放資源。

編寫main函式

int main()
{
    unzFile zfile;//定義一個unzFile型別的結構體zfile
    //定義zip檔案的路徑,可以使用相對路徑
    char filepath[]="extract/test.zip";
    //呼叫unzOpen64()函式開啟zip檔案
    zfile = unzOpen64(filepath);
    if (zfile == NULL)
    {
        cout << filepath << "[INFO] 開啟壓縮檔案失敗" << endl;
        return -1;
    }
    else
    {
        cout << "[INFO] 成功開啟壓縮檔案" << endl;
    }
    unz_global_info64 zGlobalInfo;
    //unz_global_info64是一個結構體
    //其中最重要的是number_entry成員
    //這個變量表示了壓縮檔案中的所有檔案數目(包括資料夾、
    //檔案、以及子資料夾和子資料夾中的檔案)
    //我們用這個變數來迴圈讀取zip檔案中的所有檔案
    if (UNZ_OK != unzGetGlobalInfo64(zfile, &zGlobalInfo))
    //使用unzGetGlobalInfo64函式獲取zip檔案全域性資訊
    {
        cout << "[ERROR] 獲取壓縮檔案全域性資訊失敗" << endl;
        return -1;
    }
    //迴圈讀取zip包中的檔案,
    //在extract_currentfile函式中
    //進行檔案解壓、建立、寫入、儲存等操作
    for (int i = 0; i < zGlobalInfo.number_entry; i++)
    {
        //extract_currentfile函式的第二個引數指將檔案解壓到哪裡
        //這裡使用extract/,表示將其解壓到執行目錄下的extract資料夾中
        int err = extract_currentfile(zfile, "extract/");
        //關閉當前檔案
        unzCloseCurrentFile(zfile);
        //使指標指向下一個檔案
        unzGoToNextFile(zfile);
    }
    //關閉壓縮檔案
    unzClose(zfile);
    system("pause");
    return 0;
}

編寫extract_currentfile函式

輸入引數:
1. unzFile zfile——要解壓的unzFile型別的變數
2. char * extractdirectory——解壓的目標資料夾路徑即解壓到哪裡
Return:
int

這個函式的流程如下:
1. 使用

unzGetCurrentFileInfo64(zfile, &zFileInfo, fileName_WithPath, fileName_BufSize, NULL, 0, NULL, 0)

函式獲取當前zfile檔案中指標指向的檔案的檔案資訊,儲存在unz_file_info64型別的結構體zFileInfo變數中,獲取當前檔案相對於zip檔案的相對路徑和檔名,儲存在字串fileName_WithPath中;
2. 修改fileName_WithPath變數,使得其包含解壓的目標資料夾
3. 判斷當前檔案是目錄還是檔案,如果是目錄則建立目錄,如果是檔案則解壓檔案
4. 如果是檔案,則使用 unzOpenCurrentFile函式開啟當前檔案
5. 使用fstream庫以二進位制流的形式建立當前檔案
6. 使用unzReadCurrentFile讀取當前檔案內容到一個字串中
7. 將字串寫入建立好的檔案

int extract_currentfile(unzFile zfile,char * extractdirectory)
{
    unsigned int fileName_BufSize = 512;
    char *fileName_WithPath=new char[fileName_BufSize];
    char *p,*fileName_WithoutPath;
    unz_file_info64 zFileInfo;
    p = fileName_WithoutPath = fileName_WithPath;
    if (UNZ_OK != unzGetCurrentFileInfo64(zfile,
     &zFileInfo, fileName_WithPath,
      fileName_BufSize, NULL, 0, NULL, 0))
    {
        cout << "[ERROR] 獲取當前檔案資訊失敗" << endl;
        return -1;
    }
    char *temp = new char[fileName_BufSize];
    //修改fileName_WithPath,使得extractdirectory加在其前面
    strcpy_s(temp, 512, extractdirectory);
    strcat_s(temp, 512, fileName_WithPath);
    fileName_WithPath = temp;
    //判斷當前檔案是目錄還是檔案
    while ((*p) != '\0')
    {
        if (((*p) == '/') || ((*p) == '\\'))
            fileName_WithoutPath = p + 1;
        p++;
    }
    if (*fileName_WithoutPath == '\0')
    {
        cout << "[INFO] " << "成功讀取當前目錄:" << fileName_WithPath << endl;
        cout << "[INFO] " << "開始建立目錄:" << fileName_WithPath << endl;
        //建立目錄
        int err = _mkdir(fileName_WithPath);
        if (err != 0)
            cout << "[ERROR] " << "建立目錄" 
            << fileName_WithPath << "失敗" << endl;
        else
            cout << "[INFO] " << "成功建立目錄" 
            << fileName_WithPath << endl;
    }
    else
    {
        cout << "[INFO] " << "成功讀取當前檔案:" 
        << fileName_WithoutPath << endl;
        cout << "[INFO] " << "開始解壓當前檔案:" 
        << fileName_WithoutPath << endl;
        //開啟當前檔案
        if (UNZ_OK != unzOpenCurrentFile(zfile))
        {
            //錯誤處理資訊  
            cout <<"[ERROR] "<< "開啟當前檔案" << 
            fileName_WithoutPath << "失敗!" << endl;
        }
        //定義一個fstream物件,用來寫入檔案
        fstream file;
        file.open(fileName_WithPath, ios_base::out | ios_base::binary);
        ZPOS64_T fileLength = zFileInfo.uncompressed_size;
        //定義一個字串變數fileData,讀取到的檔案內容將儲存在該變數中
        char *fileData = new char[fileLength];
        //解壓縮檔案  
        ZPOS64_T err = unzReadCurrentFile(zfile, (voidp)fileData, fileLength);
        if (err<0)
            cout << "[ERROR] " << "解壓當前檔案" 
            << fileName_WithoutPath << "失敗!" << endl;
        else
            cout << "[INFO] " << "解壓當前檔案"  
            << fileName_WithoutPath << "成功!" << endl;
        file.write(fileData, fileLength);
        file.close();
        free(fileData);
    }
    return 0;
}

執行前

需要在執行目錄下建立一個extract資料夾,將要解壓的檔案複製到該資料夾下,並對程式碼中的壓縮資料夾路徑進行修改
這裡寫圖片描述
這裡寫圖片描述

執行

這裡寫圖片描述
在/extract資料夾中檢視解壓好的檔案:
這裡寫圖片描述

在Ubuntu中實現解壓zip檔案

配置

  1. 安裝gcc&g++
  2. 下載zlib原始碼並解壓
  3. 進入zlib原始碼所在資料夾,開啟終端
  4. 執行./configure
    這裡寫圖片描述
  5. 執行make
    這裡寫圖片描述
  6. 執行sudo make install
    這裡寫圖片描述

程式碼

程式碼和windows下唯一的區別在於前面提到的建立資料夾要包含的標頭檔案和使用的函式不同。
建立一個zlibtest.cpp檔案,輸入下列程式碼

#include <iostream>
#include <zlib.h>
#include "unzip.h"
#include <cstring>
#include <fstream>
#include <sys/stat.h>
#include <sys/types.h>
using namespace std;
int extract_currentfile( unzFile zfile, char *extractdirectory )
{
    unsigned int    fileName_BufSize    = 512;
    char        *fileName_WithPath  = new char[fileName_BufSize];
    char        *p, *fileName_WithoutPath;
    unz_file_info64 zFileInfo;
    p = fileName_WithoutPath = fileName_WithPath;
    if ( UNZ_OK != unzGetCurrentFileInfo64( zfile,
                        &zFileInfo, fileName_WithPath, fileName_BufSize, NULL, 0, NULL, 0 ) )
    {
        cout << "[ERROR] 獲取當前檔案資訊失敗" << endl;
        return(-1);
    }
    char *temp = new char[fileName_BufSize];
    strcpy( temp, extractdirectory );
    strcat( temp, fileName_WithPath );
    fileName_WithPath = temp;
    while ( (*p) != '\0' )
    {
        if ( ( (*p) == '/') || ( (*p) == '\\') )
            fileName_WithoutPath = p + 1;
        p++;
    }
    if ( *fileName_WithoutPath == '\0' )
    {
        cout << "[INFO] " << "成功讀取當前目錄:" << fileName_WithPath << endl;
        cout << "[INFO] " << "開始建立目錄:" << fileName_WithPath << endl;
        int err = mkdir( fileName_WithPath, S_IRWXU | S_IRWXG | S_IROTH );
        if ( err != 0 )
            cout << "[ERROR] " << "建立目錄" << fileName_WithPath << "失敗" << endl;
        else
            cout << "[INFO] " << "成功建立目錄" << fileName_WithPath << endl;
    }else  {
        cout << "[INFO] " << "成功讀取當前檔案:" << fileName_WithoutPath << endl;
        cout << "[INFO] " << "開始解壓當前檔案:" << fileName_WithoutPath << endl;
        if ( UNZ_OK != unzOpenCurrentFile( zfile ) )
        {
/* 錯誤處理資訊 */
            cout << "[ERROR] " << "開啟當前檔案" << fileName_WithoutPath << "失敗!" << endl;
        }
        fstream file;
        file.open( fileName_WithPath, ios_base::out | ios_base::binary );
        ZPOS64_T    fileLength  = zFileInfo.uncompressed_size;
        char        *fileData   = new char[fileLength];
/* 解壓縮檔案 */
        ZPOS64_T err = unzReadCurrentFile( zfile, (voidp) fileData, fileLength );
        if ( err < 0 )
            cout << "[ERROR] " << "解壓當前檔案" << fileName_WithoutPath << "失敗!" << endl;
        else
            cout << "[INFO] " << "解壓當前檔案" << fileName_WithoutPath << "成功!" << endl;
        file.write( fileData, fileLength );
        file.close();
        free( fileData );
    }
    return(0);
}


int main()
{
    unzFile zfile;
    char    filepath[] = "./extract/test.zip";
    zfile = unzOpen64( filepath );
    if ( zfile == NULL )
    {
        cout << "[ERROR] 檔案不存在" << endl;
        return(-1);
    }else  {
        cout << "[INFO] 成功開啟壓縮檔案" << endl;
    }
    unz_global_info64 zGlobalInfo;
    if ( UNZ_OK != unzGetGlobalInfo64( zfile, &zGlobalInfo ) )
    {
        cout << "[ERROR] 獲取壓縮檔案全域性資訊失敗" << endl;
        return(-1);
    }
    for ( int i = 0; i < zGlobalInfo.number_entry; i++ )
    {
        int err = extract_currentfile( zfile, "extract/" );
        unzCloseCurrentFile( zfile );
        unzGoToNextFile( zfile );
    }
    return(0);
}

編譯

  1. 將zlib原始碼中/contrib/minizip資料夾下的檔案:
    • ioapi.c
    • ioapi.h
    • unzip.c
    • unzip.h
      複製到zlibtest.cpp所在的資料夾下
  2. 由於ioapi.c和unzip.c都是C語言檔案,不能直接和編寫的zlibtest.cpp檔案一起編譯。所以首先編譯出它們的.o目標檔案
  3. 在專案所在資料夾中開啟終端
  4. 執行gcc ./ioapi.c -c得到ioapi.o檔案
  5. 執行gcc ./unzip.c -c得到unzip,o檔案
  6. 執行
g++ ./ioapi.o ./unzip.o ./zlibtest.cpp -o zlibtest -L /usr/local/lib/libz.a /usr/local/lib/libz.so

     得到zlibtest可執行檔案
     7. 在zlibtest.cpp所在資料夾中建立一個extract資料夾,將要解壓的test.zip複製到裡面
     8. 執行./zlibtest,zip檔案就會解壓到extract檔案中
這裡寫圖片描述

凡事多問為什麼

  1. 首先,ioapi,c和unzip.c必須和zlibtest.cpp聯合編譯才能成功,因為解壓的函式都是在這兩個檔案中實現的
  2. 在上面的第6步的命令中:
g++ ./ioapi.o ./unzip.o ./zlibtest.cpp -o zlibtest -L /usr/local/lib/libz.a /usr/local/lib/libz.so

-L引數後面連結了一個靜態庫libz.a,一個動態庫libz.so
我是怎麼知道要連結這兩個庫的呢(不連結會報錯)?我是天才
檢視剛才安裝zlib的過程就可以看到,在sudo make install這一步,
這裡寫圖片描述
將一些庫檔案複製到了/usr/local/lib資料夾中,其中就包含了兩個檔案:libz.a和libz.so.1.2.8,但是在編譯命令中我使用了libz.so而並沒有使用libz.so.1.2.8.,為什麼編譯還是通過了呢?
那麼我改用libz.so.1.2.8來編譯,輸入

g++ ./ioapi.o ./unzip.o ./zlibtest.cpp -o zlibtest -L /usr/local/lib/libz.a /usr/local/lib/libz.so.1.2.8

來編譯,還是通過了。這就很奇怪了,libz.so是從哪裡來的?
首先找到libz.so所在的資料夾也就是/usr/local/lib下:
這裡寫圖片描述
這裡竟然有三個檔案:libz.so,libz.so.1,libz.so.1.2.8!開啟它們的屬性,這三個檔案竟然是同一時間建立的!而且具有相同的檔案大小!
這裡寫圖片描述
這就表示,在建立libz.so.1.2.8的時候,其它兩個檔案就一起建立了。但是在sudo make install這一步並沒有出現建立其它兩個檔案的命令,只有將libz.so.1.2.8複製到/usr/local/lib下的命令。
那麼很可能就在前一步也就是執行make命令的時候建立的。
果然在make這一條命令執行的終端輸出中發現了這個:
這裡寫圖片描述
這兩條命令:
ln -s libz.so.1.2.8 libz.so
ln -s libz.so.1.2.8 libz.so.1
就是建立libz.so和libz.so.1的命令。ln的含義就是建立一個連結,可以理解為快捷方式。
但是為什麼要建立這兩個連結呢?
在gcc的命令引數中,-l和-L是用來連結庫檔案的,關於這兩個引數的用法,參見文章:
http://www.cnblogs.com/benio/archive/2010/10/25/1860394.html
那麼,我就可以使用-lz來替代-L引數了,如下:

g++ ./ioapi.o ./unzip.o ./zlibtest.cpp -o zlibtest -lz

這裡寫圖片描述
編譯成功。
知道了-l的用法,建立libz.so.1.2.8的連結libz.so和libz.so.1的原因就很清楚了,就是為了-lz命令的使用。

在Ubuntu中實現解壓apk檔案(Update-2016.5.11)

當我使用上面的程式解壓apk檔案的時候出現了問題,解壓不出資料夾。
經過除錯,發現apk檔案在解壓過程中,無法拿到apk檔案中的目錄資訊,導致目錄無法建立,目錄中的檔案也就無法解壓了。
經過排查,我將判斷apk檔案中的當前專案是否是資料夾的邏輯修改了:使用當前的fileName_WithPath,解析出當前資料夾的路徑,然後建立。但是這樣做資料夾倒是創建出來了,不過全部是亂碼,而且很多資訊是錯誤的,創建出的資料夾的名稱中包含無效的編碼等資訊。
我將apk檔案和能正常解壓縮出文件夾的zip檔案的十六進位制做了比較,發現在檔案頭部的壓縮版本欄位,apk是0x0800,也就是8,表示是Deflate,而zip是0x0000,也就是0,表示是uncompressed。
但是經過檢視zlib的原始碼,並沒有發現能夠解決這個問題的答案。
最後,我發現,當我將目錄結構手動建立好,就能夠成功解壓了。
於是我對程式碼進行了修改:

#include <iostream>
#include <cstring>
#include <fstream>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "unzip.h"
#include <zlib.h>
using namespace std;
void mymkdir(char * file_Path)
{
    DIR *dp;
    if ((dp = opendir(file_Path)) == NULL)
    {
        int err = mkdir(file_Path,S_IRWXU|S_IRWXG|S_IROTH);
        if(err!=0)
        {
            int end=strlen(file_Path);
            char *p=&file_Path[end-1];
            while ((*p) != '/')
            {
                p--;
            }
            int length=(int)strlen(file_Path)-(int)strlen(p);
            char *temp=new char[length];
            memcpy(temp,file_Path,length);
            mymkdir(temp);
            err = mkdir(file_Path,S_IRWXU|S_IRWXG|S_IROTH);
        }
    }
    closedir(dp);
}
int makedirectory(unzFile zfile,char *extractdirectory)
{
    unsigned int fileName_BufSize=516;
    char *fileName_WithPath=new char[fileName_BufSize];
    char *file_Path=new char[fileName_BufSize];
    char *p,*fileName_WithoutPath;
    unz_file_info64 zFileInfo;
    if (UNZ_OK != unzGetCurrentFileInfo64(zfile, &zFileInfo, fileName_WithPath, fileName_BufSize, NULL, 0, NULL, 0))
    {
        return -1;
    }
    char *temp = new char[fileName_BufSize];
    strcpy(temp,extractdirectory);
    strcat(temp,fileName_WithPath);
    fileName_WithPath = temp;
    p = fileName_WithoutPath = fileName_WithPath;
    while ((*p) != '\0')
    {
        if (((*p) == '/') || ((*p) == '\\'))
        fileName_WithoutPath = p + 1;
        p++;
    }
    int length=(int)strlen(fileName_WithPath)-(int)strlen(fileName_WithoutPath);
    memcpy(file_Path,fileName_WithPath,length);
    mymkdir(file_Path);
}

int extract_currentfile(unzFile zfile,char *extractdirectory)
{
unsigned int fileName_BufSize=516;
char *fileName_WithPath=new char[fileName_BufSize];
char *p,*fileName_WithoutPath;
unz_file_info64 zFileInfo;

if (UNZ_OK != unzGetCurrentFileInfo64(zfile, &zFileInfo, fileName_WithPath, fileName_BufSize, NULL, 0, NULL, 0))
{
return -1;
}
char *temp = new char[fileName_BufSize];
strcpy(temp,extractdirectory);
strcat(temp,fileName_WithPath);
fileName_WithPath = temp;
p = fileName_WithoutPath = fileName_WithPath;
while ((*p) != '\0')
{
if (((*p) == '/') || ((*p) == '\\'))
fileName_WithoutPath = p + 1;
p++;
}
if (UNZ_OK != unzOpenCurrentFile(zfile))
{
    return -2;
}
fstream file;
file.open(fileName_WithPath, ios_base::out | ios_base::binary);
ZPOS64_T fileLength = zFileInfo.uncompressed_size;
char *fileData = new char[fileLength];
//解壓縮檔案  
ZPOS64_T err = unzReadCurrentFile(zfile, (voidp)fileData, fileLength);
if (err<0)
{
    return -3;
}
file.write(fileData, fileLength);
file.close();
free(fileData);
return 0;
}

int main()
{
unzFile zfile;
char filepath[]="./test.apk";
zfile=unzOpen64(filepath);
if(zfile==NULL)
{
cout<<"[ERROR] 檔案不存在"<<endl;
return -1;
}
else
{
cout << "[INFO] 成功開啟壓縮檔案:" <<filepath<< endl;
}
unz_global_info64 zGlobalInfo;
if (UNZ_OK != unzGetGlobalInfo64(zfile, &zGlobalInfo))
{
cout << "[ERROR] 獲取壓縮檔案全域性資訊失敗" << endl;
return -1;
}
for (int i = 0; i < zGlobalInfo.number_entry; i++)
{
int err = makedirectory(zfile, (char *)"extract/");
unzCloseCurrentFile(zfile);
unzGoToNextFile(zfile);
}
unzGoToFirstFile (zfile);
for (int i = 0; i < zGlobalInfo.number_entry; i++)
{
int err = extract_currentfile(zfile, (char *)"extract/");
unzCloseCurrentFile(zfile);
unzGoToNextFile(zfile);
}
cout<<"[INFO] 解壓完成" << endl;
return 0;
}

簡單的說就是實現了一個建立資料夾的函式makedirectory,首先呼叫該函式建立好目錄結構,然後再次解壓。不過這樣做導致解壓時間稍微有點增加,但是時間還是比較短的。

結語

既然能夠用gcc編譯成功,那麼在linux下使用QtCreator編寫解壓的程式就很簡單了,因為QtCreator用的也是gcc嘛。
至於如何壓縮檔案,基本操作也可以在網上找到,重點就在如何配置編譯環境。