使用zlib解壓.apk/.zip文件(Windows&Ubuntu)

前言

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

Git

現在所有的模塊源代碼位於github:https://github.com/LeeHDsniper/APKAnalyserModules

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
    這裏寫圖片描述
  7. 關於安裝zlib的更多信息,參見:http://www.linuxidc.com/Linux/2012-06/61982.htm

代碼

代碼和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嘛。
至於如何壓縮文件,基本操作也可以在網上找到,重點就在如何配置編譯環境。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章