• zlib的应用


    主要分析应用的demo,有空再分析底层实现

    官方demo位于zlib-1.2.11contribminizip

    对官方代码的分析

    /*
       miniunz.c
       Version 1.1, February 14h, 2010
       sample part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
    
             Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
    
             Modifications of Unzip for Zip64
             Copyright (C) 2007-2008 Even Rouault
    
             Modifications for Zip64 support on both zip and unzip
             Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
    */
    
    #if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
            #ifndef __USE_FILE_OFFSET64
                    #define __USE_FILE_OFFSET64
            #endif
            #ifndef __USE_LARGEFILE64
                    #define __USE_LARGEFILE64
            #endif
            #ifndef _LARGEFILE64_SOURCE
                    #define _LARGEFILE64_SOURCE
            #endif
            #ifndef _FILE_OFFSET_BIT
                    #define _FILE_OFFSET_BIT 64
            #endif
    #endif
    
    #ifdef __APPLE__
    // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
    #define FOPEN_FUNC(filename, mode) fopen(filename, mode)
    #define FTELLO_FUNC(stream) ftello(stream)
    #define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
    #else
    #define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
    #define FTELLO_FUNC(stream) ftello64(stream)
    #define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
    #endif
    
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <errno.h>
    #include <fcntl.h>
    
    #ifdef _WIN32
    # include <direct.h>
    # include <io.h>
    #else
    # include <unistd.h>
    # include <utime.h>
    #endif
    
    
    #include "unzip.h"
    
    #define CASESENSITIVITY (0)
    #define WRITEBUFFERSIZE (8192)
    #define MAXFILENAME (256)
    
    #ifdef _WIN32
    #define USEWIN32IOAPI
    #include "iowin32.h"
    #endif
    /*
      mini unzip, demo of unzip package
    
      usage :
      Usage : miniunz [-exvlo] file.zip [file_to_extract] [-d extractdir]
    
      list the file in the zipfile, and print the content of FILE_ID.ZIP or README.TXT
        if it exists
    */
    
    
    /* change_file_date : change the date/time of a file
        filename : the filename of the file where date/time must be modified
        dosdate : the new date at the MSDos format (4 bytes)
        tmu_date : the SAME new date at the tm_unz format */
    void change_file_date(filename,dosdate,tmu_date)
        const char *filename;
        uLong dosdate;
        tm_unz tmu_date;
    {
    #ifdef _WIN32
      HANDLE hFile;
      FILETIME ftm,ftLocal,ftCreate,ftLastAcc,ftLastWrite;
    
      hFile = CreateFileA(filename,GENERIC_READ | GENERIC_WRITE,
                          0,NULL,OPEN_EXISTING,0,NULL);
      GetFileTime(hFile,&ftCreate,&ftLastAcc,&ftLastWrite);
      DosDateTimeToFileTime((WORD)(dosdate>>16),(WORD)dosdate,&ftLocal);
      LocalFileTimeToFileTime(&ftLocal,&ftm);
      SetFileTime(hFile,&ftm,&ftLastAcc,&ftm);
      CloseHandle(hFile);
    #else
    #ifdef unix || __APPLE__
      struct utimbuf ut;
      struct tm newdate;
      newdate.tm_sec = tmu_date.tm_sec;
      newdate.tm_min=tmu_date.tm_min;
      newdate.tm_hour=tmu_date.tm_hour;
      newdate.tm_mday=tmu_date.tm_mday;
      newdate.tm_mon=tmu_date.tm_mon;
      if (tmu_date.tm_year > 1900)
          newdate.tm_year=tmu_date.tm_year - 1900;
      else
          newdate.tm_year=tmu_date.tm_year ;
      newdate.tm_isdst=-1;
    
      ut.actime=ut.modtime=mktime(&newdate);
      utime(filename,&ut);
    #endif
    #endif
    }
    
    
    /* mymkdir and change_file_date are not 100 % portable
       As I don't know well Unix, I wait feedback for the unix portion */
    
    int mymkdir(dirname)
        const char* dirname;
    {
        int ret=0;
    #ifdef _WIN32
        ret = _mkdir(dirname);
    #elif unix
        ret = mkdir (dirname,0775);
    #elif __APPLE__
        ret = mkdir (dirname,0775);
    #endif
        return ret;
    }
    
    int makedir (newdir)
        char *newdir;
    {
      char *buffer ;
      char *p;
      int  len = (int)strlen(newdir);
    
      if (len <= 0)
        return 0;
    
      buffer = (char*)malloc(len+1);
            if (buffer==NULL)
            {
                    printf("Error allocating memory
    ");
                    return UNZ_INTERNALERROR;
            }
      strcpy(buffer,newdir);
    
      if (buffer[len-1] == '/') {
        buffer[len-1] = '';
      }
      if (mymkdir(buffer) == 0)
        {
          free(buffer);
          return 1;
        }
    
      p = buffer+1;
      while (1)
        {
          char hold;
    
          while(*p && *p != '\' && *p != '/')
            p++;
          hold = *p;
          *p = 0;
          if ((mymkdir(buffer) == -1) && (errno == ENOENT))
            {
              printf("couldn't create directory %s
    ",buffer);
              free(buffer);
              return 0;
            }
          if (hold == 0)
            break;
          *p++ = hold;
        }
      free(buffer);
      return 1;
    }
    
    void do_banner()
    {
        printf("MiniUnz 1.01b, demo of zLib + Unz package written by Gilles Vollant
    ");
        printf("more info at http://www.winimage.com/zLibDll/unzip.html
    
    ");
    }
    
    void do_help()
    {
        printf("Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]
    
    " 
               "  -e  Extract without pathname (junk paths)
    " 
               "  -x  Extract with pathname
    " 
               "  -v  list files
    " 
               "  -l  list files
    " 
               "  -d  directory to extract into
    " 
               "  -o  overwrite files without prompting
    " 
               "  -p  extract crypted file using password
    
    ");
    }
    
    void Display64BitsSize(ZPOS64_T n, int size_char)
    {
      /* to avoid compatibility problem , we do here the conversion */
      char number[21];
      int offset=19;
      int pos_string = 19;
      number[20]=0;
      for (;;) {
          number[offset]=(char)((n%10)+'0');
          if (number[offset] != '0')
              pos_string=offset;
          n/=10;
          if (offset==0)
              break;
          offset--;
      }
      {
          int size_display_string = 19-pos_string;
          while (size_char > size_display_string)
          {
              size_char--;
              printf(" ");
          }
      }
    
      printf("%s",&number[pos_string]);
    }
    
    int do_list(uf)
        unzFile uf;
    {
        uLong i;
        unz_global_info64 gi;
        int err;
    
        err = unzGetGlobalInfo64(uf,&gi);
        if (err!=UNZ_OK)
            printf("error %d with zipfile in unzGetGlobalInfo 
    ",err);
        printf("  Length  Method     Size Ratio   Date    Time   CRC-32     Name
    ");
        printf("  ------  ------     ---- -----   ----    ----   ------     ----
    ");
        for (i=0;i<gi.number_entry;i++)
        {
            char filename_inzip[256];
            unz_file_info64 file_info;
            uLong ratio=0;
            const char *string_method;
            char charCrypt=' ';
            err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
            if (err!=UNZ_OK)
            {
                printf("error %d with zipfile in unzGetCurrentFileInfo
    ",err);
                break;
            }
            if (file_info.uncompressed_size>0)
                ratio = (uLong)((file_info.compressed_size*100)/file_info.uncompressed_size);
    
            /* display a '*' if the file is crypted */
            if ((file_info.flag & 1) != 0)
                charCrypt='*';
    
            if (file_info.compression_method==0)
                string_method="Stored";
            else
            if (file_info.compression_method==Z_DEFLATED)
            {
                uInt iLevel=(uInt)((file_info.flag & 0x6)/2);
                if (iLevel==0)
                  string_method="Defl:N";
                else if (iLevel==1)
                  string_method="Defl:X";
                else if ((iLevel==2) || (iLevel==3))
                  string_method="Defl:F"; /* 2:fast , 3 : extra fast*/
            }
            else
            if (file_info.compression_method==Z_BZIP2ED)
            {
                  string_method="BZip2 ";
            }
            else
                string_method="Unkn. ";
    
            Display64BitsSize(file_info.uncompressed_size,7);
            printf("  %6s%c",string_method,charCrypt);
            Display64BitsSize(file_info.compressed_size,7);
            printf(" %3lu%%  %2.2lu-%2.2lu-%2.2lu  %2.2lu:%2.2lu  %8.8lx   %s
    ",
                    ratio,
                    (uLong)file_info.tmu_date.tm_mon + 1,
                    (uLong)file_info.tmu_date.tm_mday,
                    (uLong)file_info.tmu_date.tm_year % 100,
                    (uLong)file_info.tmu_date.tm_hour,(uLong)file_info.tmu_date.tm_min,
                    (uLong)file_info.crc,filename_inzip);
            if ((i+1)<gi.number_entry)
            {
                err = unzGoToNextFile(uf);
                if (err!=UNZ_OK)
                {
                    printf("error %d with zipfile in unzGoToNextFile
    ",err);
                    break;
                }
            }
        }
    
        return 0;
    }
    
    /***
    popt_extract_without_path : 解压的时候是否需要中间的路径
    **/
    int do_extract_currentfile(uf,popt_extract_without_path,popt_overwrite,password)
        unzFile uf;
        const int* popt_extract_without_path;
        int* popt_overwrite;
        const char* password;
    {
        char filename_inzip[256];
        char* filename_withoutpath;
        char* p;
        int err=UNZ_OK;
        FILE *fout=NULL;
        void* buf;
        uInt size_buf;
    
        unz_file_info64 file_info;
        uLong ratio=0;
        err = unzGetCurrentFileInfo64(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);//filename_inzip 得到uf内部的文件名
    
        if (err!=UNZ_OK)
        {
            printf("error %d with zipfile in unzGetCurrentFileInfo
    ",err);
            return err;
        }
    
        size_buf = WRITEBUFFERSIZE;
        buf = (void*)malloc(size_buf);
        if (buf==NULL)
        {
            printf("Error allocating memory
    ");
            return UNZ_INTERNALERROR;
        }
    
        p = filename_withoutpath = filename_inzip;
        while ((*p) != '')
        {
            if (((*p)=='/') || ((*p)=='\'))
                filename_withoutpath = p+1;//得到最底层的文件名
            p++;
        }
    
        if ((*filename_withoutpath)=='')//如果底层文件名是一个目录,那么就创建目录
        {
            if ((*popt_extract_without_path)==0)
            {
                printf("creating directory: %s
    ",filename_inzip);
                mymkdir(filename_inzip);
            }
        }
        else
        {
            const char* write_filename;
            int skip=0;
    
            if ((*popt_extract_without_path)==0)
                write_filename = filename_inzip;//包含中间路径
            else
                write_filename = filename_withoutpath;//不包含中间路径
    
            err = unzOpenCurrentFilePassword(uf,password);//打开当前内部文件
            if (err!=UNZ_OK)
            {
                printf("error %d with zipfile in unzOpenCurrentFilePassword
    ",err);
            }
    
            if (((*popt_overwrite)==0) && (err==UNZ_OK))//设置为重写,并且打开成功
            {
                char rep=0;
                FILE* ftestexist;
                ftestexist = FOPEN_FUNC(write_filename,"rb");
                if (ftestexist!=NULL)//已经存在这个解压出的文件,会不停询问是否覆盖,直到得到确切答案
                {
                    fclose(ftestexist);
                    do
                    {
                        char answer[128];
                        int ret;
    
                        printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ",write_filename);
                        ret = scanf("%1s",answer);
                        if (ret != 1)
                        {
                           exit(EXIT_FAILURE);
                        }
                        rep = answer[0] ;
                        if ((rep>='a') && (rep<='z'))
                            rep -= 0x20;
                    }
                    while ((rep!='Y') && (rep!='N') && (rep!='A'));
                }
    
                if (rep == 'N')
                    skip = 1;
    
                if (rep == 'A')
                    *popt_overwrite=1;//设置popt_overwrite为全部覆盖
            }
    
            if ((skip==0) && (err==UNZ_OK))//覆盖
            {
                fout=FOPEN_FUNC(write_filename,"wb");
                /* some zipfile don't contain directory alone before file */
                if ((fout==NULL) && ((*popt_extract_without_path)==0) &&
                                    (filename_withoutpath!=(char*)filename_inzip))
                {
                    //FOPEN_FUNC(write_filename,"wb")本身是创建文件,可是如果没有现成的上级目录,创建不会成功,所以说明不存在对应的目录
                    //filename_inzip,filename_withoutpath,write_filename是同一个字符串的不同位置,所以可以任意操作其中一个
                    char c=*(filename_withoutpath-1);
                    *(filename_withoutpath-1)='';//修改了最后一个路径符为结束符,就是一个路径
                    makedir(write_filename);//创建路径
                    *(filename_withoutpath-1)=c;//还原原来的路径
                    fout=FOPEN_FUNC(write_filename,"wb");
                }
    
                if (fout==NULL)
                {
                    printf("error opening %s
    ",write_filename);
                }
            }
    
            if (fout!=NULL)
            {
                printf(" extracting: %s
    ",write_filename);
    
                do
                {
                    err = unzReadCurrentFile(uf,buf,size_buf);//8K的内存
                    if (err<0)
                    {
                        printf("error %d with zipfile in unzReadCurrentFile
    ",err);
                        break;
                    }
                    if (err>0)
                        if (fwrite(buf,err,1,fout)!=1)
                        {
                            printf("error in writing extracted file
    ");
                            err=UNZ_ERRNO;
                            break;
                        }
                }
                while (err>0);
                if (fout)
                        fclose(fout);
    
                if (err==0)//结束
                    change_file_date(write_filename,file_info.dosDate,
                                     file_info.tmu_date);//还原创建时间
            }
    
            if (err==UNZ_OK)
            {
                err = unzCloseCurrentFile (uf);
                if (err!=UNZ_OK)
                {
                    printf("error %d with zipfile in unzCloseCurrentFile
    ",err);
                }
            }
            else
                unzCloseCurrentFile(uf); /* don't lose the error */
        }
    
        free(buf);
        return err;
    }
    /**
    打开一个ZIP文件后,
    需要先使用unzGetGlobalInfo64来取得该文件的一些信息,来了解这个压缩包里一共包含了多少个文件,等等。
    目前我们用得着的就是这个文件数目。
    然后开始遍历ZIP中的文件,初始时自动会定位在第一个文件,以后处理完一个后用unzGoToNextFile来跳到下一个文件。
    
    对于每个内部文件,可用unzGetCurrentFileInfo64来查内部文件名。
    
    这个文件名和刚才zipOpenNewFileInZip的第二个参数是一样的形式,所以有可能包含路径。
    也有可能会以路径分隔符(/)结尾,表明这是个目录项(其实压缩操作的时候也可以针对目录写入这样的内部文件,上面没有做)。
    所以接下来要根据情况创建(多级)目录。
    unzGetCurrentFileInfo64的第三个参数是unz_file_info64结构,其中也有一项包含了dosDate信息,可以还原文件时间。
    
    对于非目录的内部文件,用 unzOpenCurrentFile,打开,然后unzReadCurrentFile读取文件内容,写入到真实文件中。
    unzReadCurrentFile返回 0 表示文件读取结束。
    
    
    uf :zip文件对象
    opt_extract_without_path :0或者1
    opt_overwrite :0或者1
    password :密码
    
    **/
    int do_extract(uf,opt_extract_without_path,opt_overwrite,password)
        unzFile uf;
        int opt_extract_without_path;
        int opt_overwrite;
        const char* password;
    {
        uLong i;
        unz_global_info64 gi;
        int err;
        FILE* fout=NULL;
    
        err = unzGetGlobalInfo64(uf,&gi);//获取解压信息 unz_global_info64
        if (err!=UNZ_OK)
            printf("error %d with zipfile in unzGetGlobalInfo 
    ",err);
    
        for (i=0;i<gi.number_entry;i++)
        {
            if (do_extract_currentfile(uf,&opt_extract_without_path,
                                          &opt_overwrite,
                                          password) != UNZ_OK)//对当前内部文件解压
                break;
    
            if ((i+1)<gi.number_entry)
            {
                err = unzGoToNextFile(uf);//跳到下一个内部文件
                if (err!=UNZ_OK)
                {
                    printf("error %d with zipfile in unzGoToNextFile
    ",err);
                    break;
                }
            }
        }
    
        return 0;
    }
    
    int do_extract_onefile(uf,filename,opt_extract_without_path,opt_overwrite,password)
        unzFile uf;
        const char* filename;
        int opt_extract_without_path;
        int opt_overwrite;
        const char* password;
    {
        int err = UNZ_OK;
        if (unzLocateFile(uf,filename,CASESENSITIVITY)!=UNZ_OK)
        {
            printf("file %s not found in the zipfile
    ",filename);
            return 2;
        }
    
        if (do_extract_currentfile(uf,&opt_extract_without_path,
                                          &opt_overwrite,
                                          password) == UNZ_OK)
            return 0;
        else
            return 1;
    }
    /**
    "Usage : miniunz [-e] [-x] [-v] [-l] [-o] [-p password] file.zip [file_to_extr.] [-d extractdir]
    
    " 
               "  -e  Extract without pathname (junk paths)
    " 
               "  -x  Extract with pathname
    " 
               "  -v  list files
    " 
               "  -l  list files
    " 
               "  -d  directory to extract into
    " 
               "  -o  overwrite files without prompting
    " 
               "  -p  extract crypted file using password
    
    "
               ***/
    int main(argc,argv)
        int argc;
        char *argv[];
    {
        const char *zipfilename=NULL;
        const char *filename_to_extract=NULL;
        const char *password=NULL;
        char filename_try[MAXFILENAME+16] = "";
        int i;
        int ret_value=0;
        int opt_do_list=0;
        int opt_do_extract=1;
        int opt_do_extract_withoutpath=0;
        int opt_overwrite=0;
        int opt_extractdir=0;
        const char *dirname=NULL;
        unzFile uf=NULL;
    
        do_banner();
        if (argc==1)
        {
            do_help();//提示帮助
            return 0;
        }
        else
        {
            for (i=1;i<argc;i++)
            {
                if ((*argv[i])=='-')
                {
                    const char *p=argv[i]+1;
    
                    while ((*p)!='')
                    {
                        char c=*(p++);;
                        if ((c=='l') || (c=='L'))
                            opt_do_list = 1;
                        if ((c=='v') || (c=='V'))
                            opt_do_list = 1;
                        if ((c=='x') || (c=='X'))
                            opt_do_extract = 1;
                        if ((c=='e') || (c=='E'))
                            opt_do_extract = opt_do_extract_withoutpath = 1;
                        if ((c=='o') || (c=='O'))
                            opt_overwrite=1;
                        if ((c=='d') || (c=='D'))
                        {
                            opt_extractdir=1;
                            dirname=argv[i+1];//解压到dirname目录路径
                        }
    
                        if (((c=='p') || (c=='P')) && (i+1<argc))
                        {
                            password=argv[i+1];//密码为password
                            i++;
                        }
                    }
                }
                else
                {
                    if (zipfilename == NULL)
                        zipfilename = argv[i];//zip的文件名zipfilename
                    else if ((filename_to_extract==NULL) && (!opt_extractdir))
                            filename_to_extract = argv[i] ;//如果没有指定解压路径dirname,zip文件解压为指定的名称filename_to_extract
                }
            }
        }
    
        if (zipfilename!=NULL)
        {
    
    #        ifdef USEWIN32IOAPI
            zlib_filefunc64_def ffunc;
    #        endif
    
            strncpy(filename_try, zipfilename,MAXFILENAME-1);//拷贝filename_try,防止越界
            /* strncpy doesnt append the trailing NULL, of the string is too long. */
            filename_try[ MAXFILENAME ] = '';
    
    #        ifdef USEWIN32IOAPI
            fill_win32_filefunc64A(&ffunc);
            uf = unzOpen2_64(zipfilename,&ffunc);
    #        else
            uf = unzOpen64(zipfilename);//打开zip文件
    #        endif
            if (uf==NULL)
            {
                strcat(filename_try,".zip");//如果没有成功,尝试添加后缀名.zip
    #            ifdef USEWIN32IOAPI
                uf = unzOpen2_64(filename_try,&ffunc);
    #            else
                uf = unzOpen64(filename_try);
    #            endif
            }
        }
    
        if (uf==NULL)
        {
            printf("Cannot open %s or %s.zip
    ",zipfilename,zipfilename);
            return 1;
        }
        printf("%s opened
    ",filename_try);
    
        if (opt_do_list==1)
            ret_value = do_list(uf);//查看里面的zip文件里面的目录结果
        else if (opt_do_extract==1)
        {
    #ifdef _WIN32
            if (opt_extractdir && _chdir(dirname))//如果指定了解压路径
    #else
            if (opt_extractdir && chdir(dirname))//修改当前工作目录
    #endif
            {
              printf("Error changing into %s, aborting
    ", dirname);
              exit(-1);
            }
    
            if (filename_to_extract == NULL)
                ret_value = do_extract(uf, opt_do_extract_withoutpath, opt_overwrite, password);
            else
                ret_value = do_extract_onefile(uf, filename_to_extract, opt_do_extract_withoutpath, opt_overwrite, password);//指定解压名称
        }
    
        unzClose(uf);
    
        return ret_value;
    }
    View Code

    我自己的封装

    ZipTool.h

    #pragma once
    #include <direct.h>
    #include "unzip.h"
    
    #define WRITEBUFFERSIZE 8192
    
    #if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
    #ifndef __USE_FILE_OFFSET64
    #define __USE_FILE_OFFSET64
    #endif
    #ifndef __USE_LARGEFILE64
    #define __USE_LARGEFILE64
    #endif
    #ifndef _LARGEFILE64_SOURCE
    #define _LARGEFILE64_SOURCE
    #endif
    #ifndef _FILE_OFFSET_BIT
    #define _FILE_OFFSET_BIT 64
    #endif
    #endif
    
    #ifdef __APPLE__
    // In darwin and perhaps other BSD variants off_t is a 64 bit value, hence no need for specific 64 bit functions
    #define FOPEN_FUNC(filename, mode) fopen(filename, mode)
    #define FTELLO_FUNC(stream) ftello(stream)
    #define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
    #else
    //#define FOPEN_FUNC(filename, mode) fopen64(filename, mode)
    //#define FTELLO_FUNC(stream) ftello64(stream)
    //#define FSEEKO_FUNC(stream, offset, origin) fseeko64(stream, offset, origin)
    
    #define FOPEN_FUNC(filename, mode) fopen(filename, mode)
    #define FTELLO_FUNC(stream) ftello(stream)
    #define FSEEKO_FUNC(stream, offset, origin) fseeko(stream, offset, origin)
    #endif
    
    class ZipTool
    {
    public:
        ZipTool();
        ~ZipTool();
        int UnZip(char* zipdir,char* password);//默认的目录解压
        int UnZip(char* zipdir, char* password,char* toUnZipdir);//指定的目录解压
        int Zip();
    private:
        unzFile uf = NULL;
        int do_extract(unzFile uf, int opt_extract_without_path, int opt_overwrite, const char * password);
        int do_extract_currentfile(unzFile uf, const int * popt_extract_without_path, int * popt_overwrite, const char * password);
        void change_file_date(const char * filename, uLong dosdate, tm_unz tmu_date);
        int mymkdir(const char * dirname);
        int makedir(char * newdir);
        char* UnZipdir;
    };
    View Code

    ZipTool.cpp

    #include "stdafx.h"
    #include "ZipTool.h"
    ZipTool::ZipTool()
    {
        UnZipdir = NULL;
    }
    
    
    ZipTool::~ZipTool()
    {
    }
    
    int ZipTool::UnZip(char * zipdir, char * password){
        UnZipdir = NULL;
        if (zipdir != NULL){
            uf = unzOpen64(zipdir);//打开zip文件
        }
        if (uf == NULL){
            return 1;
        }
        int ret_value = do_extract(uf, 0, 1, password);
        unzClose(uf);
        return ret_value;
    }
    /**
    toUnZipdir:加 //或
    **/
    int ZipTool::UnZip(char * zipdir, char * password, char * toUnZipdir)
    {
        if (toUnZipdir!=NULL&&!PathFileExists(CString(toUnZipdir))) {
            //int pos=strlen(toUnZipdir);
            //if((toUnZipdir[pos-1]!= '/')&&(toUnZipdir[pos - 1]!= '\'))
            //    toUnZipdir
            makedir(toUnZipdir);
        }
        if (zipdir != NULL) {
            uf = unzOpen64(zipdir);//打开zip文件
        }
        if (uf == NULL) {
            return 1;
        }
        UnZipdir = toUnZipdir;
        int ret_value = do_extract(uf, 0, 1, password);
        unzClose(uf);
        UnZipdir = NULL;
        return ret_value;
    }
    
    
    /**
    打开一个ZIP文件后,
    需要先使用unzGetGlobalInfo64来取得该文件的一些信息,来了解这个压缩包里一共包含了多少个文件,等等。
    目前我们用得着的就是这个文件数目。
    然后开始遍历ZIP中的文件,初始时自动会定位在第一个文件,以后处理完一个后用unzGoToNextFile来跳到下一个文件。
    
    对于每个内部文件,可用unzGetCurrentFileInfo64来查内部文件名。
    
    这个文件名和刚才zipOpenNewFileInZip的第二个参数是一样的形式,所以有可能包含路径。
    也有可能会以路径分隔符(/)结尾,表明这是个目录项(其实压缩操作的时候也可以针对目录写入这样的内部文件,上面没有做)。
    所以接下来要根据情况创建(多级)目录。
    unzGetCurrentFileInfo64的第三个参数是unz_file_info64结构,其中也有一项包含了dosDate信息,可以还原文件时间。
    
    对于非目录的内部文件,用 unzOpenCurrentFile,打开,然后unzReadCurrentFile读取文件内容,写入到真实文件中。
    unzReadCurrentFile返回 0 表示文件读取结束。
    
    
    uf :zip文件对象
    opt_extract_without_path :0:解压出来包含中间路径 1:解压出来不包含中间路径
    opt_overwrite :0 :不覆盖 1:覆盖
    password :密码
    
    **/
    int ZipTool::do_extract(unzFile uf,int opt_extract_without_path,int opt_overwrite,const char* password)
    {
        uLong i;
        unz_global_info64 gi;
        int err;
        FILE* fout = NULL;
    
        err = unzGetGlobalInfo64(uf, &gi);//获取解压信息 unz_global_info64
        if (err != UNZ_OK)
            printf("error %d with zipfile in unzGetGlobalInfo 
    ", err);
    
        for (i = 0; i<gi.number_entry; i++)
        {
            if (do_extract_currentfile(uf, &opt_extract_without_path,
                &opt_overwrite,
                password) != UNZ_OK)//对当前内部文件解压
                break;
    
            if ((i + 1)<gi.number_entry)
            {
                err = unzGoToNextFile(uf);//跳到下一个内部文件
                if (err != UNZ_OK)
                {
                    printf("error %d with zipfile in unzGoToNextFile
    ", err);
                    break;
                }
            }
        }
    
        return 0;
    }
    
    
    /***
    popt_extract_without_path : 解压的时候是否需要中间的路径
    **/
    int ZipTool::do_extract_currentfile(unzFile uf, const int* popt_extract_without_path, int* popt_overwrite, const char* password)
    {
        char filename_inzip[256];
        char* filename_withoutpath;
        char* p;
        int err = UNZ_OK;
        FILE *fout = NULL;
        void* buf;
        uInt size_buf;
    
        unz_file_info64 file_info;
        uLong ratio = 0;
        err = unzGetCurrentFileInfo64(uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0);//filename_inzip 得到uf内部的文件名
    
        if (err != UNZ_OK)
        {
            printf("error %d with zipfile in unzGetCurrentFileInfo
    ", err);
            return err;
        }
    
        if (UnZipdir) {
            char filename_tmpzip[256];
            sprintf(filename_tmpzip, "%s%s",UnZipdir, filename_inzip);
            //filename_inzip[0] = filename_tmpzip;
            strncpy(filename_inzip, filename_tmpzip, 256);
        }
        size_buf = WRITEBUFFERSIZE;
        buf = (void*)malloc(size_buf);
        if (buf == NULL)
        {
            printf("Error allocating memory
    ");
            return UNZ_INTERNALERROR;
        }
    
        p = filename_withoutpath = filename_inzip;
        while ((*p) != '')
        {
            if (((*p) == '/') || ((*p) == '\'))
                filename_withoutpath = p + 1;//得到最底层的文件名
            p++;
        }
    
        if ((*filename_withoutpath) == '')//如果底层文件名是一个目录,那么就创建目录
        {
            if ((*popt_extract_without_path) == 0)
            {
                printf("creating directory: %s
    ", filename_inzip);
                mymkdir(filename_inzip);
            }
        }
        else
        {
            const char* write_filename;
            int skip = 0;
    
            if ((*popt_extract_without_path) == 0)
                write_filename = filename_inzip;//包含中间路径
            else
                write_filename = filename_withoutpath;//不包含中间路径
            //if (password) {
                err = unzOpenCurrentFilePassword(uf, password);//打开当前内部文件
                if (err != UNZ_OK)
                {
                    printf("error %d with zipfile in unzOpenCurrentFilePassword
    ", err);
                }
            //}
            if (((*popt_overwrite) == 0) && (err == UNZ_OK))//设置为重写,并且打开成功
            {
                char rep = 0;
                FILE* ftestexist;
                ftestexist = FOPEN_FUNC(write_filename, "rb");
                if (ftestexist != NULL)//已经存在这个解压出的文件,会不停询问是否覆盖,直到得到确切答案
                {
                    fclose(ftestexist);
                    do
                    {
                        char answer[128];
                        int ret;
    
                        printf("The file %s exists. Overwrite ? [y]es, [n]o, [A]ll: ", write_filename);
                        ret = scanf("%1s", answer);
                        if (ret != 1)
                        {
                            exit(EXIT_FAILURE);
                        }
                        rep = answer[0];
                        if ((rep >= 'a') && (rep <= 'z'))
                            rep -= 0x20;
                    } while ((rep != 'Y') && (rep != 'N') && (rep != 'A'));
                }
    
                if (rep == 'N')
                    skip = 1;
    
                if (rep == 'A')
                    *popt_overwrite = 1;//设置popt_overwrite为全部覆盖
            }
    
            if ((skip == 0) && (err == UNZ_OK))//覆盖
            {
                fout = FOPEN_FUNC(write_filename, "wb");
                /* some zipfile don't contain directory alone before file */
                if ((fout == NULL) && ((*popt_extract_without_path) == 0) &&
                    (filename_withoutpath != (char*)filename_inzip))
                {
                    //FOPEN_FUNC(write_filename,"wb")本身是创建文件,可是如果没有现成的上级目录,创建不会成功,所以说明不存在对应的目录
                    //filename_inzip,filename_withoutpath,write_filename是同一个字符串的不同位置,所以可以任意操作其中一个
                    char c = *(filename_withoutpath - 1);
                    *(filename_withoutpath - 1) = '';//修改了最后一个路径符为结束符,就是一个路径
                    makedir((char*)write_filename);//创建路径
                    *(filename_withoutpath - 1) = c;//还原原来的路径
                    fout = FOPEN_FUNC(write_filename, "wb");
                }
    
                if (fout == NULL)
                {
                    printf("error opening %s
    ", write_filename);
                }
            }
    
            if (fout != NULL)
            {
                printf(" extracting: %s
    ", write_filename);
    
                do
                {
                    err = unzReadCurrentFile(uf, buf, size_buf);//8K的内存
                    if (err<0)
                    {
                        printf("error %d with zipfile in unzReadCurrentFile
    ", err);
                        break;
                    }
                    if (err>0)
                        if (fwrite(buf, err, 1, fout) != 1)
                        {
                            printf("error in writing extracted file
    ");
                            err = UNZ_ERRNO;
                            break;
                        }
                } while (err>0);
                if (fout)
                    fclose(fout);
    
                if (err == 0)//结束
                    change_file_date(write_filename, file_info.dosDate,
                        file_info.tmu_date);//还原创建时间
            }
    
            if (err == UNZ_OK)
            {
                err = unzCloseCurrentFile(uf);
                if (err != UNZ_OK)
                {
                    printf("error %d with zipfile in unzCloseCurrentFile
    ", err);
                }
            }
            else
                unzCloseCurrentFile(uf); /* don't lose the error */
        }
        free(buf);
        return err;
    }
    
    
    /* change_file_date : change the date/time of a file
    filename : the filename of the file where date/time must be modified
    dosdate : the new date at the MSDos format (4 bytes)
    tmu_date : the SAME new date at the tm_unz format */
    void ZipTool::change_file_date(const char *filename,uLong dosdate,tm_unz tmu_date){
    #ifdef _WIN32
        HANDLE hFile;
        FILETIME ftm, ftLocal, ftCreate, ftLastAcc, ftLastWrite;
    
        hFile = CreateFileA(filename, GENERIC_READ | GENERIC_WRITE,
            0, NULL, OPEN_EXISTING, 0, NULL);
        GetFileTime(hFile, &ftCreate, &ftLastAcc, &ftLastWrite);
        DosDateTimeToFileTime((WORD)(dosdate >> 16), (WORD)dosdate, &ftLocal);
        LocalFileTimeToFileTime(&ftLocal, &ftm);
        SetFileTime(hFile, &ftm, &ftLastAcc, &ftm);
        CloseHandle(hFile);
    #else
    #ifdef unix || __APPLE__
        struct utimbuf ut;
        struct tm newdate;
        newdate.tm_sec = tmu_date.tm_sec;
        newdate.tm_min = tmu_date.tm_min;
        newdate.tm_hour = tmu_date.tm_hour;
        newdate.tm_mday = tmu_date.tm_mday;
        newdate.tm_mon = tmu_date.tm_mon;
        if (tmu_date.tm_year > 1900)
            newdate.tm_year = tmu_date.tm_year - 1900;
        else
            newdate.tm_year = tmu_date.tm_year;
        newdate.tm_isdst = -1;
    
        ut.actime = ut.modtime = mktime(&newdate);
        utime(filename, &ut);
    #endif
    #endif
    }
    
    int ZipTool::mymkdir(const char* dirname){
        int ret = 0;
    #ifdef _WIN32
        ret = _mkdir(dirname);
    #elif unix
        ret = mkdir(dirname, 0775);
    #elif __APPLE__
        ret = mkdir(dirname, 0775);
    #endif
        return ret;
    }
    
    int ZipTool::makedir(char *newdir){
        char *buffer;
        char *p;
        int  len = (int)strlen(newdir);
    
        if (len <= 0)
            return 0;
    
        buffer = (char*)malloc(len + 1);
        if (buffer == NULL)
        {
            printf("Error allocating memory
    ");
            return UNZ_INTERNALERROR;
        }
        strcpy(buffer, newdir);
    
        if (buffer[len - 1] == '/') {
            buffer[len - 1] = '';
        }
        if (mymkdir(buffer) == 0)
        {
            free(buffer);
            return 1;
        }
    
        p = buffer + 1;
        while (1)
        {
            char hold;
    
            while (*p && *p != '\' && *p != '/')
                p++;
            hold = *p;
            *p = 0;
            if ((mymkdir(buffer) == -1) && (errno == ENOENT))
            {
                printf("couldn't create directory %s
    ", buffer);
                free(buffer);
                return 0;
            }
            if (hold == 0)
                break;
            *p++ = hold;
        }
        free(buffer);
        return 1;
    }
    
    
    int ZipTool::Zip()
    {
        return 0;
    }
    View Code

     调用代码

    ZipTool* zip = new ZipTool();
    //zip->UnZip("C:\Users\ZhouXiaodong\Desktop\001星星.zip",NULL);
    zip->UnZip("C:\Users\ZhouXiaodong\Desktop\001星星.zip",NULL,"./001星星/");

     注意,官方提供的库 zlibwapi.dll 是32位的,会出现异常,64位库需要自己编译

    编译路径,zlib-1.2.11contribvstudio,里面有各个版本的源码

    在密码解压的时候遇见

    err = unzOpenCurrentFilePassword(uf, password);

    报错

    #define UNZ_PARAMERROR                  (-102)
    #define UNZ_BADZIPFILE                  (-103)

    https://blog.csdn.net/fyyyr/article/details/79252677

  • 相关阅读:
    (萌O(∩_∩)O)哈希知识点小结
    hash应用以及vector的使用简介:POJ 3349 Snowflake Snow Snowflakes
    BestCoder Round #3HDU 4907
    搜索:POJ2251&POJ1426&POJ3087&POJ2488
    母函数初学四大题
    欧拉函数知识点总结及代码模板及欧拉函数表
    欧几里德与扩展欧几里德算法以及青蛙的约会~
    KMP 知识点总结
    HDU 1142 A Walk Through the Forest(dijkstra+记忆化DFS)
    HDU 1535 Invitation Cards(SPFA,及其优化)
  • 原文地址:https://www.cnblogs.com/baldermurphy/p/9753510.html
Copyright © 2020-2023  润新知