在 ZIP 文件的 End Of Central Directory Record 结构中,有一个数据保存了中央目录起始位置的偏移量,我们可以通过它来获取中央目录的位置。
Offset
Byte
Description
…
…
…
16
4
中央目录起始位置的偏移量
20
2
注释长度
22
可变长度
注释内容
保存中央目录起始位置指针的数据在文件末尾,和 ZIP 文件的注释信息相邻,可以通过文件长度减去注释信息获得其位置:
1 2 3 4 5 6 7 8 9
// 通过 Go 的 ZIP API 获取到注释长度 commentLength := int64(len(zipFile.Comment))
// 当 ZIP 文件没有注释时,End Of Central Directory Record 的原始长度为 22 // 所以 End Of Central Directory Record 的实际长度为注释长度 + 22 endOfCentralDirectoryLength := commentLength + 22 endOfCentralDirectoryOffset := file.Length() - endOfCentralDirectoryLength
val commentLength = ZipFile(PATH).comment?.toByteArray()?.size ?: 0 val endOfCentralDirectoryLength = commentLength + 22 val endOfCentralDirectoryOffset = file.length() - endOfCentralDirectoryLength
val centralDirectoryPointer = endOfCentralDirectoryOffset + 16 val centralDirectoryOffset = file.readAt(centralDirectoryPointer, 4).toIntLE() val magic = file.readAt((centralDirectoryOffset - 16).toLong(), 16).decodeToString()
if (magic != "APK Sig Block 42") { println("Not v2 signature") return }
// 尾部数据区长度的起始位置偏移 val idValueBlockOffset2 = centralDirectoryOffset - 24 val idValueBlockLength = file.readAt(idValueBlockOffset2.toLong(), 8).toLongLE() // 头部数据区长度的起始位置偏移 val idValueBlockOffset1 = idValueBlockOffset2 - idValueBlockLength + 16
这里已经获取到头部和尾部数据区长度的起始位置偏移,可以开始遍历读取了:
1 2 3 4 5 6 7 8 9 10 11 12 13
// 计算数据区起始位置的偏移量 var offset = idValueBlockOffset1 + 8 while (offset < idValueBlockOffset2) { val length = file.readAt(offset, 8).toLongLE() val id = file.readAt(offset + 8, 4).toIntLE() val value = file.readAt(offset + 12, length.toInt() - 4)