zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    PAT (Advanced Level) Practice 1054 The Dominant Color (20 分)
    PAT (Advanced Level) Practice 1005 Spell It Right (20 分) (switch)
    PAT (Advanced Level) Practice 1006 Sign In and Sign Out (25 分) (排序)
    hdu 5114 Collision
    hdu4365 Palindrome graph
    单链表查找最大值、两个递增的链表合并并且去重
    蓝桥杯-最短路 (SPFA算法学习)
    蓝桥杯-最大最小公倍数
    Codeforces-470 div2 C题
    蓝桥杯-地宫取宝
  • 原文地址:https://www.cnblogs.com/baldermurphy/p/9753510.html
Copyright © 2011-2022 走看看