2013-07-05 3 views
2

Мне нужно проанализировать zip-файлы, чтобы проверить, насколько велик объем содержимого, но ZipEntry.getSize() сохраняет возврат -1. Это соответствует спецификации, если исходный размер неизвестен, но по какой-то причине 7-zip, похоже, знает фактический размер, поскольку он отображается, если я открываю его с помощью zip.Получение фактического размера записей zip

Кто-нибудь знает, как это делает 7-zip? Это просто оценка?

+0

Возможно, 7-Zip расширяет каждую запись в памяти, чтобы найти раздутый размер. –

+0

Хотя возможно, скорость, с которой он открывает большие застежки-молнии, делает это сомнительным – nablex

ответ

2

Возможно, ZipEntry обрабатывает только локальные заголовки файлов, а не центральный каталог, который записывается в конце zip-архива после сжатия, и должен содержать фактическую информацию о размере файла.

+1

Это кажется точным, я написал некоторый код для синтаксического анализа центрального каталога и правильных размеров. Благодаря! – nablex

0

Для тех, кто заинтересован, вот код, который я использовал для разбора почтовых индексов (имейте в виду, что почтовый индекс немногочисленный). Я использовал wikipedia (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 копии из существующего кода ZipEntry на основе снимок JDK 7:

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; 
} 
+0

Фактически zip-файл содержит запись CentralDirectoryLocator, которая указывает на начало центрального каталога и количество записей в нем. Таким образом, вы должны сначала пропустить несколько байтов (до килобайта или около того) с конца и seak для центрального локатора локатора, а затем - искать начало центрального каталога и читать его. –

Смежные вопросы