Лучший способ определить, упакован ли поток в Java - PullRequest
19 голосов
/ 27 ноября 2009

Как лучше всего узнать, что я java.io.InputStream содержит сжатые данные?

Ответы [ 5 ]

37 голосов
/ 14 марта 2014

Введение

Поскольку всем ответам 5 лет, я чувствую обязанность записать, что происходит сегодня. Я серьезно сомневаюсь, что нужно читать магические байты потока! Это код низкого уровня, его следует избегать в целом.

Простой ответ

Мику пишет:

Если поток может быть прочитан через ZipInputStream, он должен быть заархивирован.

Да, но в случае ZipInputStream «может быть прочитано» означает, что первый вызов .getNextEntry() возвращает ненулевое значение. Не исключение ловли и так далее. Поэтому вместо разбора магических байтов вы можете просто:

boolean isZipped = new ZipInputStream(yourInputStream).getNextEntry() != null;

И это все!

Общие мысли о расстегивании молнии

В целом оказалось, что работать с файлами при [un] zip-архивировании гораздо удобнее, чем с потоками. Есть несколько полезных библиотек, плюс ZipFile обладает большей функциональностью, чем ZipInputStream. Обработка zip-файлов обсуждается здесь: Что такое хорошая библиотека Java для архивирования / распаковки файлов? Так что, если вы можете работать с файлами, вам лучше это делать!

Пример кода

Мне нужно было в моем приложении работать только с потоками. Вот метод, который я написал для распаковки:

import org.apache.commons.io.IOUtils;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

public boolean unzip(InputStream inputStream, File outputFolder) throws IOException {

    ZipInputStream zis = new ZipInputStream(inputStream);

    ZipEntry entry;
    boolean isEmpty = true;
    while ((entry = zis.getNextEntry()) != null) {
        isEmpty = false;
        File newFile = new File(outputFolder, entry.getName());
        if (newFile.getParentFile().mkdirs() && !entry.isDirectory()) {
            FileOutputStream fos = new FileOutputStream(newFile);
            IOUtils.copy(zis, fos);
            IOUtils.closeQuietly(fos);
        }
    }

    IOUtils.closeQuietly(zis);
    return !isEmpty;
}
22 голосов
/ 27 ноября 2009

Волшебные байты для формата ZIP: 50 4B. Вы можете протестировать поток (используя mark и reset - вам может понадобиться buffer ), но я не ожидаю, что это будет 100% надежный подход. Не будет никакого способа отличить его от текстового файла в кодировке US-ASCII, который начинается с букв PK.

Наилучшим способом будет * предоставление метаданных в формате содержимого до открытия потока, а затем соответствующая обработка.

6 голосов
/ 27 ноября 2009

Можно проверить, что первые четыре байта потока являются локальной подписью заголовка файла , которая запускает локальный заголовок файла , который обрабатывает каждый файл в файле ZIP, как показано в спецификации здесь , чтобы быть 50 4B 03 04.

Небольшой тестовый код показывает, как это работает:

byte[] buffer = new byte[4];

try {
    ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("so.zip"));
    ZipEntry ze = new ZipEntry("HelloWorld.txt");
    zos.putNextEntry(ze);
    zos.write("Hello world".getBytes());
    zos.close();

    FileInputStream is = new FileInputStream("so.zip");
    is.read(buffer);
    is.close();
}
catch(IOException e) {
    e.printStackTrace();
}

for (byte b : buffer) { 
    System.out.printf("%H ",b);
}

дал мне этот вывод:

50 4B 3 4 
5 голосов
/ 27 ноября 2009

Не очень элегантно, но надежно:

Если поток может быть прочитан через ZipInputStream, он должен быть заархивирован.

0 голосов
/ 12 ноября 2015

Проверка магического числа может быть неправильным вариантом.

Файлы Docx также имеют схожее магическое число 50 4B 3 4

...