获取zip条目的实际大小

时间:2013-07-05 11:36:41

标签: java zip 7zip zipfile

我必须分析zip文件以检查其中的内容有多大,但ZipEntry.getSize()会一直返回-1。这是根据规格,如果原始尺寸未知但由于某种原因7-zip似乎知道实际尺寸,如果我用它打开拉链显示它。

有谁知道7-zip是怎么做到的?它只是估计吗?

2 个答案:

答案 0 :(得分:3)

也许ZipEntry只处理本地文件头,而不是在压缩完成后写在zip存档末尾的中心目录,并且应该包含实际的文件大小信息。

答案 1 :(得分:0)

对于那些感兴趣的人,这里是我用来解析拉链的代码(请记住,zip是小端)。我使用维基百科(http://en.wikipedia.org/wiki/ZIP_%28file_format%29)作为结构的参考。

public static List<ZipCentralFileHeader> getCentralDirectory(File file) throws IOException {
    List<ZipCentralFileHeader> entries = new ArrayList<ZipCentralFileHeader>();
    FileInputStream input = new FileInputStream(file);
    try {
        // only check the last 10 meg, make sure this is large enough depending on your data
        long sizeToSkip = Math.max(0, file.length() - (1024 * 1024 * 10));
        if (sizeToSkip > 0)
            input.skip(sizeToSkip);
        byte [] buffer = new byte[(int) (file.length() - sizeToSkip)];
        int read = input.read(buffer);
        if (read != buffer.length)
            throw new IOException("Could not read the necessary data");
        for (int i = 0; i < buffer.length - 4; i++) {
            if (buffer[i] == 0x50 && buffer[i + 1] == 0x4b && buffer[i + 2] == 0x01 && buffer[i + 3] == 0x02) {
                Date lastModified = dosToJavaTime(get32(buffer, i + 12));
                long compressedSize = get32(buffer, i + 20);
                long uncompressedSize = get32(buffer, i + 24);
                int nameLength = get16(buffer, i + 28);
                int extraFieldLength = get16(buffer, i + 30);
                int commentLength = get16(buffer, i + 32);

                String fileName = new String(Arrays.copyOfRange(buffer, i + 46, i + 46 + nameLength), "UTF-8");
                String comment = new String(Arrays.copyOfRange(buffer, i + 46 + nameLength + extraFieldLength, i + 46 + nameLength + extraFieldLength + commentLength), "UTF-8");

                entries.add(new ZipCentralFileHeader(fileName, lastModified, compressedSize, uncompressedSize, comment));
            }
            // the end of the central directory
            else if (buffer[i] == 0x50 && buffer[i + 1] == 0x4b && buffer[i + 2] == 0x05 && buffer[i + 3] == 0x06) { //0x06054b50
                // each header starts the same, there is no general start sequence for the entire central directory
                // as such you can't really be sure you got them all unless you scan the entire file
                // the trailing section however contains the necessary information to validate the amount
                int amountOfFileHeaders = get16(buffer, i + 8);
                if (amountOfFileHeaders != entries.size())
                    throw new IOException("Could only read " + entries.size() + "/" + amountOfFileHeaders + " headers for " + file + ", you likely did not read enough of the file");
                break;
            }
        }
    }
    finally {
        input.close();
    }
    return entries;
}

实用程序方法get16,get32,get64和dosToJavaTime是基于jdk 7快照的现有ZipEntry代码的副本:

private static final int get16(byte b[], int off) {
    return (b[off] & 0xff) | ((b[off+1] & 0xff) << 8);
}

private static final long get32(byte b[], int off) {
    return (get16(b, off) | ((long)get16(b, off+2) << 16)) & 0xffffffffL;
}

private static final long get64(byte b[], int off) {
    return get32(b, off) | (get32(b, off+4) << 32);
}

@SuppressWarnings("deprecation")
private static Date dosToJavaTime(long dtime) {
    Date date = new Date((int)(((dtime >> 25) & 0x7f) + 80),
                      (int)(((dtime >> 21) & 0x0f) - 1),
                      (int)((dtime >> 16) & 0x1f),
                      (int)((dtime >> 11) & 0x1f),
                      (int)((dtime >> 5) & 0x3f),
                      (int)((dtime << 1) & 0x3e));
    return date;
}