前言

在上一篇的文章中,我们简单地介绍了在OTA升级中,如何对升级包文件进行加解密的方法。今天,我们继续介绍如何对文件进行解压缩。


一、为什么要解压缩?

我们知道,OTA升级的过程,简单说,就是在客户端从服务器获取升级包的过程。但是,当升级包文件过于庞大时(例如我们的手机系统升级),这个过程就会被极大地延长,从而造成非常不好的用户体验。因此,在将升级包文件上传服务器前进行一个压缩操作,可以有效地解决这个问题。客户端先在服务器下载升级包压缩文件后,接着在本地进行一个解压缩的操作,就可以得到完整的升级包文件。


二、实现步骤

1.本地实现对升级包文件的压缩

我们知道,在linux系统中,有很多命令可以实现对于文件的解压缩。如常见的gzip和gunzip,zip和unzip命令等,都可以实现对文件的压缩。因此,在上传升级包文件前的压缩文件操作,我们可以直接使用这些命令来实现,不用专门去写对应的代码来实现这个功能。
在这里,我们使用zip和unzip命令来实现对升级包的压缩。

zip updateOTA.zip updateOTA -r

我们可以看到我们已经成功地对文件进行了压缩。

2.客户端的解压缩操作

在本地,我们可以直接使用命令行工具来对文件进行压缩。那么,在客户端,如何实现解压缩操作呢?如何编程实现unzip命令的解压缩功能呢?这里我们可以到网上找到unzip的源码,稍加改动直接移植到我们的代码上即可。
在网上查找了相关的资料后,我们找到了zlib这个开源库,里面或许有我们需要的东西。


如图,我下载的版本是zlib-1.2.8,下载地址可以到github上面去搜。
将当前路径移到contrib/minizip/目录下(如下图)


我们可以看到,有unzip.c和unzip.h两个文件,可能是我们正在找的unzip命令行工具的源码。有Makefile文件,ok我们先编译下整份文件。

make all


编译完了后,我们可以看到有一些警告,本着“警告=没问题”的原则,我们继续往下走。

ls查看当前目录后,我们可以看到当前目录多出来了一些可执行文件和.o文件。


多出来的可执行程序有minizip和miniunz两个,哪个是unzip.c和unzip.h编译出来的呢?其实看文件名猜都猜出来了。保险点,我们可以看Makefile确认下。

CC=cc CFLAGS=-O -I../.. UNZ_OBJS = miniunz.o unzip.o ioapi.o ../../libz.a ZIP_OBJS = minizip.o zip.o ioapi.o ../../libz.a .c.o: $(CC) -c $(CFLAGS) $*.c all: miniunz minizip miniunz: $(UNZ_OBJS) $(CC) $(CFLAGS) -o $@ $(UNZ_OBJS) minizip: $(ZIP_OBJS) $(CC) $(CFLAGS) -o $@ $(ZIP_OBJS) test: miniunz minizip ./minizip test readme.txt ./miniunz -l test.zip mv readme.txt readme.old ./miniunz test.zip clean: /bin/rm -f *.o *~ minizip miniunz

可以看到,miniunz是我们想要的东西,同时Makefile有test使用说明,我们使用miniunz执行程序看下是否可以解压我们前面用zip工具压缩的压缩包。


可以看到,使用miniunz是可以正常解压缩压缩包的。那么,接下来,我们的工作就是看如何移植miniunz的源码到我们的代码上。
我们从上面的Makefile文件,可以看到miniunz的依赖关系。

UNZ_OBJS = miniunz.o unzip.o ioapi.o ../../libz.a

这里依赖于三个文件,一个lib.a,unzip.c和ioapi.c,我们直接将这三个文件移到我们的代码中即可。接下来就是如何修改移植miniunz.c文件。
我们直接查看miniunz.c的main函数,如下:

int main(argc,argv) int argc; char *argv[]; { int i; int opt_overwrite=0; int opt_compress_level=Z_DEFAULT_COMPRESSION; int opt_exclude_path=0; int zipfilenamearg = 0; char filename_try[MAXFILENAME+16]; int zipok; int err=0; int size_buf=0; void* buf=NULL; const char* password=NULL; do_banner(); if (argc==1) { do_help(); return 0; } else { for (i=1;i='0') && (c<='9')) opt_compress_level = c-'0'; if ((c=='j') || (c=='J')) opt_exclude_path = 1; if (((c=='p') || (c=='P')) && (i+1='a') && (rep<='z')) rep -= 0x20; } while ((rep!='Y') && (rep!='N') && (rep!='A')); if (rep=='N') zipok = 0; if (rep=='A') opt_overwrite = 2; } } if (zipok==1) { zipFile zf; int errclose; # ifdef USEWIN32IOAPI zlib_filefunc64_def ffunc; fill_win32_filefunc64A(&ffunc); zf = zipOpen2_64(filename_try,(opt_overwrite==2) ? 2 : 0,NULL,&ffunc); # else zf = zipOpen64(filename_try,(opt_overwrite==2) ? 2 : 0); # endif if (zf == NULL) { printf("error opening %sn",filename_try); err= ZIP_ERRNO; } else printf("creating %sn",filename_try); for (i=zipfilenamearg+1;(i='0') || (argv[i][1]<='9'))) && (strlen(argv[i]) == 2))) { FILE * fin; int size_read; const char* filenameinzip = argv[i]; const char *savefilenameinzip; zip_fileinfo zi; unsigned long crcFile=0; int zip64 = 0; zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour = zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0; zi.dosDate = 0; zi.internal_fa = 0; zi.external_fa = 0; filetime(filenameinzip,&zi.tmz_date,&zi.dosDate); /* err = zipOpenNewFileInZip(zf,filenameinzip,&zi, NULL,0,NULL,0,NULL / * comment * /, (opt_compress_level != 0) ? Z_DEFLATED : 0, opt_compress_level); */ if ((password != NULL) && (err==ZIP_OK)) err = getFileCrc(filenameinzip,buf,size_buf,&crcFile); zip64 = isLargeFile(filenameinzip); /* The path name saved, should not include a leading slash. */ /*if it did, windows/xp and dynazip couldn't read the zip file. */ savefilenameinzip = filenameinzip; while( savefilenameinzip[0] == '\' || savefilenameinzip[0] == '/' ) { savefilenameinzip++; } /*should the zip file contain any path at all?*/ if( opt_exclude_path ) { const char *tmpptr; const char *lastslash = 0; for( tmpptr = savefilenameinzip; *tmpptr; tmpptr++) { if( *tmpptr == '\' || *tmpptr == '/') { lastslash = tmpptr; } } if( lastslash != NULL ) { savefilenameinzip = lastslash+1; // base filename follows last slash. } } /**/ err = zipOpenNewFileInZip3_64(zf,savefilenameinzip,&zi, NULL,0,NULL,0,NULL /* comment*/, (opt_compress_level != 0) ? Z_DEFLATED : 0, opt_compress_level,0, /* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */ -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, password,crcFile, zip64); if (err != ZIP_OK) printf("error in opening %s in zipfilen",filenameinzip); else { fin = FOPEN_FUNC(filenameinzip,"rb"); if (fin==NULL) { err=ZIP_ERRNO; printf("error in opening %s for readingn",filenameinzip); } } if (err == ZIP_OK) do { err = ZIP_OK; size_read = (int)fread(buf,1,size_buf,fin); if (size_read < size_buf) if (feof(fin)==0) { printf("error in reading %sn",filenameinzip); err = ZIP_ERRNO; } if (size_read>0) { err = zipWriteInFileInZip (zf,buf,size_read); if (err<0) { printf("error in writing %s in the zipfilen", filenameinzip); } } } while ((err == ZIP_OK) && (size_read>0)); if (fin) fclose(fin); if (err<0) err=ZIP_ERRNO; else { err = zipCloseFileInZip(zf); if (err!=ZIP_OK) printf("error in closing %s in the zipfilen", filenameinzip); } } } errclose = zipClose(zf,NULL); if (errclose != ZIP_OK) printf("error in closing %sn",filename_try); } else { do_help(); } free(buf); return 0; }

可以看到源代码,我们接下来的工作就简单很多了,我们直接将这里的main函数注释掉,重新封装一个unzip_file函数用于外部调用解压文件。函数实现如下:

/* * return * 0: success other: error */ int unzip_file(char *zipFileName, char *srcPath) { const char *password=NULL; char filename_try[MAXFILENAME+16] = ""; int ret_value=0; int opt_do_extract_withoutpath=0; int opt_overwrite=0; unzFile uf=NULL; char zipfilename[64] = {0}; strcat(zipfilename, srcPath); strcat(zipfilename, zipFileName); if (zipfilename!=NULL) { #ifdef USEWIN32IOAPI zlib_filefunc64_def ffunc; #endif strncpy(filename_try, zipfilename,MAXFILENAME-1); /* 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); #endif if (uf==NULL) { strcat(filename_try,".zip"); #ifdef USEWIN32IOAPI uf = unzOpen2_64(filename_try,&ffunc); #else uf = unzOpen64(filename_try); #endif } } if (uf==NULL) { printf("Cannot open %s or %s.zipn",zipfilename,zipfilename); return 1; } printf("%s openedn",filename_try); ret_value = do_extract(uf, opt_do_extract_withoutpath, opt_overwrite, password); unzClose(uf); return ret_value; }

最后,编译,并移植到开发板测试OK。


总结

今天就暂时讲到这里,后面有时间我们再继续讲如何对文件校验这一块的内容。