前言
在上一篇的文章中,我们简单的介绍了在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<argc;i++)
{
if ((*argv[i])=='-')
{
const char *p=argv[i]+1;
while ((*p)!='\0')
{
char c=*(p++);;
if ((c=='o') || (c=='O'))
opt_overwrite = 1;
if ((c=='a') || (c=='A'))
opt_overwrite = 2;
if ((c>='0') && (c<='9'))
opt_compress_level = c-'0';
if ((c=='j') || (c=='J'))
opt_exclude_path = 1;
if (((c=='p') || (c=='P')) && (i+1<argc))
{
password=argv[i+1];
i++;
}
}
}
else
{
if (zipfilenamearg == 0)
{
zipfilenamearg = i ;
}
}
}
}
size_buf = WRITEBUFFERSIZE;
buf = (void*)malloc(size_buf);
if (buf==NULL)
{
printf("Error allocating memory\n");
return ZIP_INTERNALERROR;
}
if (zipfilenamearg==0)
{
zipok=0;
}
else
{
int i,len;
int dot_found=0;
zipok = 1 ;
strncpy(filename_try, argv[zipfilenamearg],MAXFILENAME-1);
/* strncpy doesnt append the trailing NULL, of the string is too long. */
filename_try[ MAXFILENAME ] = '\0';
len=(int)strlen(filename_try);
for (i=0;i<len;i++)
if (filename_try[i]=='.')
dot_found=1;
if (dot_found==0)
strcat(filename_try,".zip");
if (opt_overwrite==2)
{
/* if the file don't exist, we not append file */
if (check_exist_file(filename_try)==0)
opt_overwrite=1;
}
else
if (opt_overwrite==0)
if (check_exist_file(filename_try)!=0)
{
char rep=0;
do
{
char answer[128];
int ret;
printf("The file %s exists. Overwrite ? [y]es, [n]o, [a]ppend : ",filename_try);
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')
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 %s\n",filename_try);
err= ZIP_ERRNO;
}
else
printf("creating %s\n",filename_try);
for (i=zipfilenamearg+1;(i<argc) && (err==ZIP_OK);i++)
{
if (!((((*(argv[i]))=='-') || ((*(argv[i]))=='/')) &&
((argv[i][1]=='o') || (argv[i][1]=='O') ||
(argv[i][1]=='a') || (argv[i][1]=='A') ||
(argv[i][1]=='p') || (argv[i][1]=='P') ||
((argv[i][1]>='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 zipfile\n",filenameinzip);
else
{
fin = FOPEN_FUNC(filenameinzip,"rb");
if (fin==NULL)
{
err=ZIP_ERRNO;
printf("error in opening %s for reading\n",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 %s\n",filenameinzip);
err = ZIP_ERRNO;
}
if (size_read>0)
{
err = zipWriteInFileInZip (zf,buf,size_read);
if (err<0)
{
printf("error in writing %s in the zipfile\n",
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 zipfile\n",
filenameinzip);
}
}
}
errclose = zipClose(zf,NULL);
if (errclose != ZIP_OK)
printf("error in closing %s\n",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 ] = '\0';
#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.zip\n",zipfilename,zipfilename);
return 1;
}
printf("%s opened\n",filename_try);
ret_value = do_extract(uf, opt_do_extract_withoutpath, opt_overwrite, password);
unzClose(uf);
return ret_value;
}
最后,编译,并移植到开发板测试OK。
总结
今天就暂时讲到这里,后面有时间我们再继续讲如何对文件校验这一块的内容。