当前位置: 首页 > news >正文

zip压缩包的格式不标准导致C++开源unzip.cpp解压失败问题的排查

目录

1、问题描述

2、初步排查

3、查看错误码512对应的含义

4、直接将解压zip包的函数拷贝过来,并将无法解压的zip取来,直接编写测试代码去调试解压过程,最终定位问题

4.1、调试开源unzip.cpp源码的准备工作

4.2、刚解压zip包中最顶层的文件夹就失败了

4.3、是不是zip包根路径中包含了一个文件夹有问题?手动压缩一个zip包根路径不是文件夹包测试一下

5、企业微信创建的zip包的格式是不标准的,导致unzip.cpp解压失败

6、最后


C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931C/C++实战进阶(专栏文章已更新400多篇,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.htmlWindows C++ 软件开发从入门到精通(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12695902.htmlVC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585C++软件分析工具从入门到精通案例集锦(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795开源组件及数据库技术(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12458859.html网络编程与网络问题分享(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_2276111.html        最近在项目中遇到的一个zip包解压失败的问题,是因为zip压缩包的数据格式不标准,导致使用C++开源unzip.cpp解压失败,本文分享一下这一问题的排查过程。

1、问题描述

       最近测试人员在测试客户端软件的在线升级功能,先将软件zip升级包上传到平台侧的升级服务器上,然后客户端软件登录后发起版本检测,检测到新版本后发起立即下载,下载完成后点击立即安装:

报升级包解压失败,在线升级终止。

       之前在线升级功能一直是正常的,第一次遇到这种zip升级包解压失败的问题,着实有点奇怪的。因为新版本即将发布,这个问题要尽快排查。

在线升级的功能已经开发很多年了,从来没遇到过升级包下载完成后解压失败的问题,经最终排查找出的原因,也能解释为啥之前从来没有出现这类问题。

2、初步排查

       从出问题的机器上取来了软件运行日志,看到了如下的报错信息:

压缩包解压失败,返回的错误码为512。

       先是到zip包的存放路径中找到了zip包:

确定zip包是下载成功的,即解压之前本地是有这个zip包的。然后确认了一下解压后的存放路径,程序是有权限读写这个路径的,所以可以排查没有读写权限的问题。

       于是取来下载路径的zip包,直接用我本地安装的压缩工具去解压,是可以正常解压出来的,这就奇怪了,为啥代码中使用C++开源的unzip.cpp去解压就有问题呢?难道当前的zip包使用了新的格式,而C++开源的unzip.cpp的版本有点老,识别不了新的格式,导致解压失败了?


       在这里,给大家重点推荐一下我的几个热门畅销专栏,欢迎订阅:(博客主页还有其他专栏,可以去查看)

专栏1:(该精品技术专栏的订阅量已达到500多个,专栏中包含大量项目实战分析案例,有很强的实战参考价值,广受好评!专栏文章持续更新中,预计更新到200篇以上!欢迎订阅!)

C++软件调试与异常排查从入门到精通系列文章汇总icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/125529931

本专栏根据多年C++软件异常排查的项目实践,系统地总结了引发C++软件异常的常见原因以及排查C++软件异常的常用思路与方法,详细讲述了C++软件的调试方法与手段,以图文并茂的方式给出具体的项目问题实战分析实例(很有实战参考价值),带领大家逐步掌握C++软件调试与异常排查的相关技术,适合基础进阶和想做技术提升的相关C++开发人员!

考察一个开发人员的水平,一是看其编码及设计能力,二是要看其软件调试能力!所以软件调试能力(排查软件异常的能力)很重要,必须重视起来!能解决一般人解决不了的问题,既能提升个人能力及价值,也能体现对团队及公司的贡献!

专栏中的文章都是通过项目实战总结出来的,包含大量项目问题实战分析案例,有很强的实战参考价值!专栏文章还在持续更新中,预计文章篇数能更新到200篇以上!

专栏2:  

C++常用软件分析工具从入门到精通案例集锦汇总(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/131405795

常用的C++软件辅助分析工具有SPY++、PE工具、Dependency Walker、GDIView、Process Explorer、Process Monitor、API Monitor、Clumsy、Windbg、IDA Pro等,本专栏详细介绍如何使用这些工具去巧妙地分析和解决日常工作中遇到的问题,很有实战参考价值!

专栏3:(本专栏涵盖了多方面的内容,是当前重点打造的专栏,专栏文章已经更新到400多篇,持续更新中...)

C/C++实战进阶(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_11931267.html

以多年的开发实战为基础,总结并讲解一些的C/C++基础与项目实战进阶内容,以图文并茂的方式对相关知识点进行详细地展开与阐述!专栏涉及了C/C++领域多个方面的内容,包括C++基础及编程要点(模版泛型编程、STL容器及算法函数的使用等)、C++11及以上新特性(不仅看开源代码会用到,日常编码中也会用到部分新特性,面试时也会涉及到)、常用C++开源库的介绍与使用、代码分享(调用系统API、使用开源库)、常用编程技术(动态库、多线程、多进程、数据库及网络编程等)、软件UI编程(Win32/duilib/QT/MFC)、C++软件调试技术(排查软件异常的手段与方法、分析C++软件异常的基础知识、常用软件分析工具使用、实战问题分析案例等)、设计模式、网络基础知识与网络问题分析进阶内容等。

专栏4:   

VC++常用功能开发汇总(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/124272585

将10多年C++开发实践中常用的功能,以高质量的代码展现出来。这些常用的高质量规范代码,可以直接拿到项目中使用,能有效地解决软件开发过程中遇到的问题。

专栏5: 

Windows C++ 软件开发从入门到精通(专栏文章,持续更新中...)icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/category_12695902.html

根据多年C++软件开发实践,详细地总结了Windows C++ 应用软件开发相关技术实现细节,分享了大量的实战案例,很有实战参考价值。


3、查看错误码512对应的含义

       直接打开unzip.cpp文件查看源代码,随便找了一个返回值的宏,然后go到定义处,找到这些返回值的定义的地方:

​错误码512,对应的十六进制数为0x00000200,其宏定义为ZR_NOFILE,其含义为:couldn't create/open the file,无法创建或打开文件。

       一般遇到创建或打开文件失败,可能是对所在目录没有操作权限所致的,但上面已经确认程序对解压目录是有操作权限的,即能创建或打开文件的。于是到代码中搜索ZR_NOFILE,有好几个地方都返回了这个错误码,但因为对unzip.cpp的内部代码逻辑不清楚,很难确定是哪一处的代码返回的。

4、直接将解压zip包的函数拷贝过来,并将无法解压的zip取来,直接编写测试代码去调试解压过程,最终定位问题

4.1、调试开源unzip.cpp源码的准备工作

       因为上述问题是在具体的业务场景中的,不方便在业务场景中调试。于是直接将无法解压的zip包拷贝到我机器上,并将解压zip包的函数拷贝到方便调试的项目主工程中,然后编写测试代码,直接去调试开源的unzip.cpp的源码。

       需要将unzip.h和unzip.cpp两个文件添加到当前测试的项目主工程中:

​并且在解决方案中找到添加的unzip.cpp文件,右键点击之,打开文件的属性页面:

在预编译头中设置unzip.cpp不使用预编译头。如果不设置,会报找不到预编译头文件的错误:

​         调试开源unzip.cpp源码的测试代码及解压zip包的函数代码如下所示:

tstring strDstPath = _T("D:\\ziptest");
tstring strZipPacth = _T("D:\\ziptest\\zylink_update_files.zip");
Unzip(strDstPath, strZipPacth);// strDstPath为解压后的文件存放路径,strZipName为待解压的zip包路径(绝对路径)
BOOL Unzip(const tstring& strDstPath, const tstring& strZipName)
{TCHAR achLog[256] = { 0 };// 先检查路径是否存在,不存在则创建BOOL bExsit = ::PathFileExists(strDstPath.c_str());if (!bExsit){CreateDirectory(strDstPath.c_str(), NULL);}SetCurrentDirectory(strDstPath.c_str());INS_UNZIP::HZIP hz = INS_UNZIP::OpenZip(strZipName.c_str(), 0);if (hz == NULL){_stprintf(achLog, _T("[Unzip]UPD_UNZIP::OpenZip打开%s失败,压缩包解压失败"), strZipName.c_str());WriteUpdateLog(achLog);return FALSE;}INS_UNZIP::ZIPENTRY ze;INS_UNZIP::GetZipItem(hz, -1, &ze);int numitems = ze.index;for (int i = 0; i < numitems; i++){INS_UNZIP::GetZipItem(hz, i, &ze);DWORD dwRet = INS_UNZIP::UnzipItem(hz, i, ze.name);if (dwRet != 0) // 解压有错误{INS_UNZIP::CloseZipU(hz);_stprintf(achLog, _T("[Unzip]UPD_UNZIP::UnzipItem解压%s失败,压缩包解压失败,错误码:%d"), ze.name, dwRet);WriteUpdateLog(achLog);return FALSE;}}INS_UNZIP::CloseZipU(hz);return TRUE;
}

        此外,上述代码中使用到了tstring类型,这是我使用宏自定义的,指向的其实就是C++标准库中的string和wstring类:

#ifdef _UNICODE
#define tstring wstring  // UNICODE宽字节 
#else
#define tstring string    // ANSI窄字节
#endif

4.2、刚解压zip包中最顶层的文件夹就失败了

        结合代码及打印日志,可以看到,刚解压zip包中最顶层的文件夹zylink_update_files就失败了:(以360压缩软件打开zip包查看根路径下的zylink_update_files)

​于是在封装的解压zip包的函数中添加断点如下:

​中断下来后就可以调试进unzip.cpp开源代码内部了。

       于是编译代码,开启调试,命中断点,进入UnzipItem函数内部去调试,最终发现在内部接口TUnzip::Unzip中返回了ZR_NOFILE:

​单步调试发现,是调用CreateFile接口时创建文件失败了,紧接着就返回ZR_NOFILE了。但是从现有的代码上下文,还是没法确定是哪里有问题。

4.3、是不是zip包根路径中包含了一个文件夹有问题?手动压缩一个zip包根路径不是文件夹包测试一下

       难道是不是zip包根路径中包含了一个文件夹zylink_update_files,解压就有问题?于是手动压缩一个zip包,该zip包根路径不是一个单一的文件夹,然后用当前的代码测试一下,看看能否完整解压出来。

       测试了一下,确实解压出来了,并且压缩包中也包含几个文件夹,于是在现有代码中加入if测试代码:

看看在解压I18N这个文件夹时代码是怎么走的,看看和问题最开始的压缩包根路径下的文件夹zylink_update_files的解压,有什么不一样的地方。

       启动调试,命中断点,调试进入UnzipItem函数内部代码,发现解压I18N时,根据属性值判断其是个文件夹,直接走文件夹的处理分支,如下所示:

​在目标路径(存放解压出来的文件路劲)中将目录创建出来,就直接return了,没有走下面的处理单个文件的分支代码。

       奇怪,按讲最开始的问题中,压缩包的根路径下的zylink_update_files,应该也是个文件夹,为啥当时代码没有走处理文件夹的分支,而是直接走处理单个文件的分支?

​于是查看代码,看看是如何判断当前对象是个文件夹的?是根据ze.attr的值16,该值就等于FILE_ATTRIBUTE_DIRECTORY宏的值,所以判断当前处理对象是个文件夹

      于是将zip包恢复成之前有问题的zip包,重新调试代码,当在解压压缩包根路径中的zylink_update_files时,ze.attr的值居然是0

​所以不等于FILE_ATTRIBUTE_DIRECTORY,所以代码走到处理单个文件的分支。

       按讲如果是创建以zylink_update_files为名称的文件,应该是能创建成功的,为啥会创建失败呢?经调试发现,在unzip.cpp内部函数中会解析出待解压对象的路径和名称,调用CreateFile时传入的是一个路径,如下所示:

​这个路径是个文件夹的路径,所以调用CreateFile肯定会失败的。

5、企业微信创建的zip包的格式是不标准的,导致unzip.cpp解压失败

       之所以zylink_update_files.zip包解压失败,是因为在解压压缩包根路径下的zylink_update_files文件夹时,发现该文件夹的ze.attr为0(正常情况下,ze.attr对应的值应为16,即等于FILE_ATTRIBUTE_DIRECTORY,标记当前要解压的item是个文件夹),根据ze.attr的值为0,判断当前待解压的item:zylink_update_files,不是文件夹,所以走处理单个文件的代码分支,导致zylink_update_files解压失败,从而终止了整个压缩包的解压操作。

       根据压缩包根路径下的zylink_update_files的ze.attr的值为0,判断当前待解压的压缩包的格式可能是不标准的手动使用解压软件解压这个压缩包,是可以解压出来的,但unzip.cpp解压有问题,说明unzip.cpp相对解压缩软件,兼容性要差一些!

后来尝试用解压缩软件将zylink_update_files.zip包解压出来,然后再使用解压缩软件重新压缩,重新压缩后的zylink_update_files.zip包,unzip.cpp是可以正常解压的!

        既然怀疑当前有问题的zylink_update_files.zip包的格式不是标准的,所以询问测试人员,当时上传到升级服务器上的压缩包是用什么工具生成的?测试人员说,当时是直接将磁盘上的文件夹:

直接拖到企业微信中,企业微信会自动压缩,生成zip包:

​然后平台服务器侧的运维人员直接取企业微信生成的zip包,上传到升级服务器上的。

       所以怀疑企业微信生成的zip包格式不是标准的所以让测试人员重新使用解压缩软件去生成zip包,用这个新的升级包去测试。经验证,使用解压缩软件生成的zip包,客户端中的unzip.cpp就可以正常解压了。

6、最后

       之前一直没问题的功能,在经历不同人的不同处理方式之后,可能就会出问题了。只能通过逐步的排查,找到引发问题的原因,才能最终搞清楚一直没问题的功能为什么此次会出问题。

       遇到必现的问题,且可以调试代码,是最幸福的事!在排查问题的过程中,必要时可以将异常情况与正常情况对比一下,看看有什么不同,不同点一般就是问题的突破口可能问题的场景比较复杂,我们为了方便调试,可以添加测试代码,人为的构造调试环境(比如本问题中直接将问题压缩包拿过来,编写解压指定路径下问题压缩包的测试代码)。

       对于构造问题复现环境的场景,比如某个问题在连不上某个服务器时才出现(在客户的环境中才有这个问题),可以在我们公司的测试环境中用clumsy拦截数据包(通过ip和端口设置过滤条件),让本地程序故意登录不上业务服务器,去复现和排查问题!对应案例,我已经写成了文章,可以查看:

使用Process Explorer和Clumsy工具定位软件高CPU占用问题icon-default.png?t=N7T8https://blog.csdn.net/chenlycly/article/details/130038272

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • loginApi
  • 每天五分钟深度学习pytorch:训练神经网络模型的基本步骤
  • 【竞品分析】竞品分析报告的基本模板
  • 裁剪或填充张量(Tensor)(四维与五维)(Python代码)
  • 【Python】requests的response.text 和 urllib.request 的 response.read()的区别
  • 编程-设计模式 26:业务代表模式
  • Android11 关于三方应用获取su权限
  • 以Zed项目为例学习大型Rust项目的组织与管理
  • 数据结构----二叉树
  • Cairo库移植到安卓记录
  • 【登录扫码】--集成企业微信
  • todoList清单(HTML+CSS+JavaScript)
  • Linux中apache服务安装与mysql安装
  • git强制推送代码教程
  • 如何使用css写三角形
  • JS 中的深拷贝与浅拷贝
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • bootstrap创建登录注册页面
  • Docker 笔记(1):介绍、镜像、容器及其基本操作
  • PAT A1120
  • Python学习笔记 字符串拼接
  • tensorflow学习笔记3——MNIST应用篇
  • 安卓应用性能调试和优化经验分享
  • 机器学习 vs. 深度学习
  • 基于Javascript, Springboot的管理系统报表查询页面代码设计
  • 漂亮刷新控件-iOS
  • 如何用vue打造一个移动端音乐播放器
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 使用 Docker 部署 Spring Boot项目
  • 小程序测试方案初探
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • ​软考-高级-信息系统项目管理师教程 第四版【第14章-项目沟通管理-思维导图】​
  • ​油烟净化器电源安全,保障健康餐饮生活
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • (LLM) 很笨
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (五)IO流之ByteArrayInput/OutputStream
  • (一)、软硬件全开源智能手表,与手机互联,标配多表盘,功能丰富(ZSWatch-Zephyr)
  • (转)ObjectiveC 深浅拷贝学习
  • ../depcomp: line 571: exec: g++: not found
  • .dat文件写入byte类型数组_用Python从Abaqus导出txt、dat数据
  • .NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库
  • .net framework4与其client profile版本的区别
  • .Net FrameWork总结
  • .net 调用php,php 调用.net com组件 --
  • .net 后台导出excel ,word
  • .NET 设计模式初探
  • .Net多线程Threading相关详解
  • .NET国产化改造探索(三)、银河麒麟安装.NET 8环境
  • .NET企业级应用架构设计系列之开场白
  • .NET设计模式(2):单件模式(Singleton Pattern)
  • .net生成的类,跨工程调用显示注释
  • [2016.7.Test1] T1 三进制异或
  • [Android] Binder 里的 Service 和 Interface 分别是什么
  • [android] 请求码和结果码的作用