Лучший способ определить, заархивирован ли поток на Java
-
05-07-2019 - |
Вопрос
Каков наилучший способ узнать, что я java.io.InputStream
содержит архивированные данные?
Решение
магические байты для формата ZIP составляют 50 4B код>. Вы можете протестировать поток (используя пометку и сброс - вам может понадобиться буферизовать ), но Я не ожидал бы, что это будет 100% надежный подход. Не будет никакого способа отличить его от текстового файла в кодировке US-ASCII, который начинается с букв
PK
.
Наилучшим способом было бы предоставить метаданные в формате содержимого до открытия потока и затем обработать его соответствующим образом.
Другие советы
Введение
Поскольку всем ответам 5-летней давности, я считаю своим долгом записать, что происходит сегодня.Я серьезно сомневаюсь, что стоит читать магические байты потока!Это низкоуровневый код, его вообще следует избегать.
Простой ответ
мику пишет:
Если поток может быть прочитан через ZipInputStream, он должен быть заархивирован.
Да, но в случае ZipInputStream
"может быть прочитан" означает, что первый вызов .getNextEntry()
возвращает ненулевое значение.Никаких исключений из правил и так далее.Итак, вместо синтаксического анализа магических байтов вы можете просто сделать:
boolean isZipped = new ZipInputStream(yourInputStream).getNextEntry() != null;
И это все!
Общие мысли о распаковке
В общем, оказалось, что работать с файлами во время [распаковки] архивирования гораздо удобнее, чем с потоками.Существует несколько полезных библиотек, плюс 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;
}
Вы можете проверить, что первые четыре байта потока являются локальной подписью заголовка файла , которая запускает локальный заголовок файла , который обрабатывает каждый файл в файле ZIP, < a href = "http://www.pkware.com/documents/casestudies/APPNOTE.TXT" rel = "noreferrer">, как показано в спецификации здесь , чтобы быть 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
Не очень элегантно, но надежно:
Если поток можно прочитать через ZipInputStream
, он должен быть заархивирован.
Проверка магического числа может быть неправильным вариантом.
Файлы Docx также имеют похожее магическое число 50 4B 3 4