[Zlib]_[初级]_[使用zlib库解压提取文件]

时间:2021-11-16 02:43:41


场景:

1. zlib库跨平台,Windows和MacOSX都可以使用,还支持64位编译,轻量级,没有不用的道理。

2. 处理.zip,apk,docx文件时,因为这类文件都是zip格式,使用zlib能方便提取压缩文件里的数据。


方法:

1. 除了要使用zlib库本身,还需要使用zlib里的contrib目录minizip部分头文件和.c文件。

zlib-1.2.5/src/contrib/minizip/unzip.h
unzip.c,ioapi.c,ioapi.h (windows还需要 iowin32.h, iowind32.c)

2. 例子,读取压缩文件里的某个文件到内存std::string, 基本原理就是打开zip文件,定位需要读取的文件,之后读取这个文件,关闭zip文件。

以下代码只支持Mac.

#include "zip/zip_util.h"

#include <iostream>
#include <string>
#include <algorithm>
#include <stdlib.h>

#include <zlib.h>
#include "zip/unzip.h"

using namespace std;

#define WRITEBUFFERSIZE (1242880) // 5Mb buffer

string ZipUtil::ReadZipFile(string zipFile, string fileInZip) {
    int err = UNZ_OK;                 // error status
    uInt size_buf = WRITEBUFFERSIZE;  // byte size of buffer to store raw csv data
    void* buf;                        // the buffer
    string sout;                      // output strings
    char filename_inzip[256];         // for unzGetCurrentFileInfo
    unz_file_info file_info;          // for unzGetCurrentFileInfo
    
    unzFile uf = unzOpen64(zipFile.c_str()); // open zipfile stream
    if (uf==NULL) {
        cerr << "Cannot open " << zipFile << endl;
        return sout;
    } // file is open
    
    if ( unzLocateFile(uf,fileInZip.c_str(),1) ) { // try to locate file inside zip
        // second argument of unzLocateFile: 1 = case sensitive, 0 = case-insensitive
        cerr << "File " << fileInZip << " not found in " << zipFile << endl;
        return sout;
    } // file inside zip found
    
    if (unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0)) {
        cerr << "Error " << err << " with zipfile " << zipFile << " in unzGetCurrentFileInfo." << endl;
        return sout;
    } // obtained the necessary details about file inside zip
    
    buf = (void*)malloc(size_buf); // setup buffer
    if (buf==NULL) {
        cerr << "Error allocating memory for read buffer" << endl;
        return sout;
    } // buffer ready
    
    err = unzOpenCurrentFilePassword(uf,NULL); // Open the file inside the zip (password = NULL)
    if (err!=UNZ_OK) {
        cerr << "Error " << err << " with zipfile " << zipFile << " in unzOpenCurrentFilePassword." << endl;
        return sout;
    } // file inside the zip is open
    
    // Copy contents of the file inside the zip to the buffer
    cout << "Extracting: " << filename_inzip << " from " << zipFile << endl;
    do {
        err = unzReadCurrentFile(uf,buf,size_buf);
        if (err<0) {
            cerr << "Error " << err << " with zipfile " << zipFile << " in unzReadCurrentFile" << endl;
            sout = ""; // empty output string
            break;
        }
        // copy the buffer to a string
        if (err>0) for (int i = 0; i < (int) err; i++) sout.push_back( *(((char*)buf)+i) );
    } while (err>0);
    
    err = unzCloseCurrentFile (uf);  // close the zipfile
    if (err!=UNZ_OK) {
        cerr << "Error " << err << " with zipfile " << zipFile << " in unzCloseCurrentFile" << endl;
        sout = ""; // empty output string
    }
    unzClose(uf);
    free(buf); // free up buffer memory
    return sout;
}

Windows的路径需要Unicode才能支持中文路径,所以用以下的UnzOpen64实现:

unzFile ZipUtil::UnzOpen64(const char* path)
{
	zlib_filefunc64_def ffunc;  
    fill_win32_filefunc64W(&ffunc);  
    wchar_t* temp_path = Utf82Unicode(path);  
    unzFile zf = unzOpen2_64(temp_path,&ffunc);
	return zf;
}