解压缩zip文件报错
背景
之前做了一个自动解压缩各种类型压缩包的软件,昨天发现有4%的zip压缩包会爆出Bad magic number for central directory的错误。
经过
经过测试,在windows和ubuntu的桌面版解压软件下,可以正常解压,在ubuntu的unzip和我写的代码下,会报错。
在github,google,stackoverflow等地方进行搜索,发现之前曾经出现过该问题,不过较少人遇到,也基本没有解决方案。
stackoverflow下,给出一个解决方案就是用已经成熟的软件,如7zip, jar, rar等命令行的解压缩方式。
结果
我在源代码中定位了报错的地方,经过了一系列的zip解压缩格式的原理的了解和对zip解压代码的测试。
在zip中,PK\x03\x04是文件的前四个字节,是代表他是个zip压缩文件的magci number,以及文件头部的一些信息,但这里没用到。
PK\x05\x06到文件结尾的总共22个字节是zip文件尾部的magic number,后面的18个字节是zip文件的一些相关信息,主要放着zip包里的文件个数,central directory相对文件起始处的偏移量,central directory的长度这些信息。
其中central directory是一个目录样的结构,存放里面压缩的文件或者文件夹信息,一般在文件尾部那22个字节的前面,每一个文件/文件夹的条目都以PK\x01\x02这个魔数来开头,如果不一致就会发生我们遇到的Bad magic number for central directory的报错,其中每一个条目长度包括魔数是46个字节,包含了文件/文件夹的一些基础信息,比如压缩前压缩后的长度,创建时间,在zip包中的位置等信息,紧跟在每一个条目后面的就是文件名,文件名的长度也在前面条目的信息里。zipfile这个库会根据这些东西来找到文件的基础信息,用来解压。
在我们测试的压缩包中,均存在一个zip文件内出现了两个central directory和后面的PK\x05\x06尾部数据,其中第二个PK\x05\x06也就是文件末尾部分,给出的是不正确的相关信息,central directory的位置和长度均不正确,所以unzip和python的zipfile库用的这个信息获取了错误的entral directory的位置,出现的第二个central directory也有一些问题,他出现了已经被删除的文件的信息,其他相关信息似乎也和第一个central directory不一致。
我将第二个central directory和后面的尾部信息舍去,只留下第一个,就可以正常解压了。
猜测可能是在压缩包之内删除文件遇到了什么奇怪的缓存不一致之类的问题,导致最后那些表的信息被混乱重复的第二次追加到zip文件的尾部。
正常的解压代码是这样的:
with zipfile.ZipFile(file, 'r') as zip: zip.extractall('/your/path')
我们只需要修改成:
import io fp = open(file_path, 'rb') #读出zip文件的数据,排除掉尾部数据 data = fp.read()[:-22] fp.close() #找到从右向左的看,第二个尾部数据 index = data.rfind(b'PK\005\006') + 22 #只剩下真正的压缩包数据 data = io.BytesIO(data[:index]) #zipfile可以用byte io来读取数据,和上面的效果一样 with zipfile.ZipFile(data, 'r') as zip: zip.extractall('/your/path')