Подтвердить что ты не робот

Uncompressing ZIP файл в памяти на Java

Я загружаю zipped файлы, содержащие XML файлы, и я бы хотел избежать записи zip файлов на диск, прежде чем манипулировать ими из-за требований к задержкам. Однако java.util.zip мне не хватает. Невозможно сказать "здесь байтовый массив zip файла, используйте его", не превращая его в поток, а ZipInputStream не является надежным, поскольку он сканирует заголовки записей (см. Обсуждение ниже EDIT по причинам, почему это не так надежный).

У меня пока нет доступа к zip файлам, которые я буду обрабатывать, поэтому я не знаю, смогу ли я обработать их через ZipInputStream, и мне нужно найти решение, которое будет работать с любыми действительными ZIP файлами, поскольку штраф за провал, как только я пойду в производство, будет высоким.

Предполагая, что ZipInputStream не будет работать, что я могу сделать, чтобы решить эту проблему в тех случаях, когда нет заголовков записей? Я использую определение Википедии, в котором содержится комментарий о том, как правильно распаковать zip файлы (приведенные ниже) в качестве стандарта.

ИЗМЕНИТЬ

У библиотеки Apache Commons Zip есть хорошая запись по некоторым проблемам, использующим Stream (как их решение, так и Java). Я также добавлю, из википедии и личного опыта, и поле размера и crc на заголовках заголовков не может быть заполнено (у меня есть файлы с -1 в этих полях). Благодаря centic за предоставление этой ссылки.

Кроме того, позвольте мне процитировать википедию по теме:

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

Обратите внимание, что ZipInputStream сканирует записи, а не центральный каталог, что является проблемой с ним.

Финальное редактирование

Если кому-то интересно, этот script можно использовать для создания действительного ZIP файла, который не может быть прочитан ZipInputStream из существующий файл ZIP. Итак, в качестве окончательного редактирования этого закрытого вопроса мне нужна библиотека, которая может читать файлы, например, созданные этим script.

4b9b3361

Ответ 1

EDIT: Еще одно предложение...

Глядя на ZipFile из реализации Apache Commons, похоже, не будет слишком сложно эффективно развить это для вашего проекта. Создайте обертку вокруг вашего байтового массива, который имеет все части API RandomAccessFile, которые требуются (я не думаю, что их очень много). Вы уже указали, что предпочитаете интерфейс ZipFile, так почему бы не пойти с этим?

Мы не знаем достаточно о вашем проекте, чтобы узнать, откроет ли он какие-либо юридические вопросы, - и даже если вы представили детали, я сомневаюсь, что кто-то здесь сможет дать хорошую юридическую консультацию, но я подозреваю, что это не будет больше часа или двух, чтобы получить это решение и работать, и я подозреваю, что у вас будет разумная уверенность в этом.


EDIT: Это может быть немного более продуктивный ответ...

Если вы беспокоитесь о том, что записи не являются смежными, но не хотите самостоятельно обрабатывать всю сторону сжатия, вы можете рассмотреть вариант, когда вы эффективно переписываете данные. Создайте новый ByteArrayOutputStream и прочитайте центральную директорию в конце. Для каждой записи в центральном каталоге выпишите запись (заголовок + данные) в выходной поток в формате, который, по вашему мнению, будет ZipInputStream. Затем напишите новый центральный каталог - если вы хотите, чтобы ваша замена была действительной, вам может потребоваться сделать это с нуля, но если вы используете код, который, как вы знаете, фактически не будет читать центральный каталог, вы можете просто предоставить исходный код, игнорируя тот факт, что он не может быть тогда действительным. Пока он начинается с правильной подписи, возможно, достаточно хорошо:)

Как только вы это сделаете, преобразуйте ByteArrayOutputStream в новый byte[], оберните его в ByteArrayInputStream, а затем перейдите к ZipInputStream или ZipArchiveInputStream.

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

Это подразумевает понимание формата zip файла, но не полностью - просто скелет, эффективно. Это не быстрое и простое решение, как использование существующего API полностью, но это не займет много времени. Это не гарантирует, что он сможет читать все недопустимые файлы (как это может быть?), Но он защитит вас от проблемы "данных между записями", о которой вы, как представляется, особенно обеспокоены. Надеюсь, это хотя бы полезная идея...


нет способа сказать "здесь массив байтов zip файла, используйте его"

Да, есть:

byte[] data = ...;
ByteArrayInputStream byteStream = new ByteArrayInputStream(data);
ZipInputStream zipStream = new ZipInputStream(byteStream);

Это оставляет вопрос о том, может ли ZipInputStream обрабатывать все zip файлы, которые вы ему дадите, но я бы не записывал их довольно быстро.

Конечно, есть и другие API. Например, вы можете посмотреть Apache Commons Compress. Несмотря на то, что для ZipFile требуется файл, ZipArchiveInputStream нет - так, вы можете использовать ByteArrayInputStream. EDIT: Похоже, что ZipArchiveStream не читается из центрального каталога. Я надеялся, что он будет использовать markSupported для проверки заранее, но, похоже, не...

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

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

Это не то же самое, что и входные данные. Он говорит, что могут быть дополнительные данные в неудобных местах, а не в том, что записи могут отсутствовать полностью. Это в основном говорит о том, что записи не следует считать смежными. Я мог бы с радостью согласиться с тем, что ZipInputStream может не читать центральный каталог в конце файла, но найти код, который делает это не то же самое, что найти код, который справляется с отсутствующими входными данными.

Затем вы пишете:

Я мог бы еще добавить, что независимо от того, является ли zip действительным или нет, это не мое беспокойство. Работа с ним.

... который предлагает вам код, который будет обрабатывать недопустимые ZIP файлы. В сочетании с этим:

У меня пока нет доступа к zip файлам, которые я буду обрабатывать, поэтому я не знаю, смогу ли я обработать их через поток

Это означает, что вы запрашиваете код, который должен обрабатывать zip файлы, которые являются недопустимыми способами, которые вы даже не можете предсказать. Насколько он недействителен для вас, чтобы иметь возможность отклонить его? Если я дам вам 1000 случайных байтов, не пытаясь им вообще быть zip файлом, что бы вы сделали с ним?

В принципе, вам нужно приложить проблему более жестко, прежде чем станет возможным даже сказать, является ли конкретная библиотека допустимым решением. Разумно собрать набор zip файлов из разных мест, которые могут быть недействительными хорошо понятными способами и сказать: "Я должен быть в состоянии поддерживать все это". Позже вам может понадобиться сделать какую-то работу, если окажется, что это было недостаточно. Но иметь возможность поддерживать что угодно, как бы он ни был сломан, просто не является действительным требованием.

Ответ 2

TrueZIP библиотека предоставляет альтернативную зрелую реализацию zip.

Он также имеет абстракцию файловой системы даже для HTTP.

Например:

Path path = new TPath(new URI("http://acme.com/download/everything.zip/entry.xml"));
try (InputStream in = Files.newInputStream(path)) {
    // Read archive entry contents here.
    ...
}

Итак, если вас интересуют только конкретные записи, он будет загружать их только, сохраняя пропускную способность и время. И вам не придется писать код загрузки.

См. также http://truezip.java.net/faq.html#http.

Ответ 3

Я бы использовал библиотеку Apache commons-compress, см. http://commons.apache.org/compress/

У него есть поддержка для чтения Zip файлов через потоки, имеется подробная документация на http://commons.apache.org/compress/zip.html для подробной документации. В нем также указаны некоторые ограничения, присущие Zip-Format.

Пример кода выглядит следующим образом:

ZipArchiveInputStream zip =
    new ZipArchiveInputStream(inputStream);
try {
    ZipArchiveEntry entry = zip.getNextZipEntry();
    while(entry != null) {
        assertEquals("README", entry.getName());
        ...
        entry = zip.getNextZipEntry();
    }
} finally {
    zip.close();
}

Ответ 4

Этот вопрос похож на Как создать каталог в памяти? псевдо файловая система/виртуальный каталог. В основном, мое предложение состоит в том, чтобы использовать более общее решение - виртуальную файловую систему в памяти (и я не имею в виду уровень ОС, например, ramfs Linux/tmpfs).

Одним из примеров является использование API-интерфейсов Java 7 NIO, которые теперь предоставляют SPI для реализации файловой системы через FileSystemProvider. Кажется, что файловая система ShrinkWrap реализует этот SPI.

Более доступным вариантом будет использование Apache Commons VFS 'файловая система ram: для этого требуется только Java 5. Если вам нужно быть совместимым с Java 5 и 6, это, вероятно, лучший выбор.

Сначала я помню, как читал о файловых системах в памяти на Java из в этой статье, который помимо указания таких решений, как Commons VFS и JBoss Microcontainer, дает хороший пример использования для среды IDE NetBeans.

В то время как виртуальная файловая система в памяти - это общее общее решение, позволяющее избежать файловой системы на уровне ОС (с соответствующими преимуществами производительности), вероятно, она страдает от других недостатков, к которым могут обратиться более специализированные решения. Например, я не уверен, как использование этой файловой системы будет вести себя при одновременном использовании нескольких потоков. Он может работать нормально, если вы не имеете доступа к тем же файлам, или вам может потребоваться создать отдельные файловые системы (что может быть непомерно с точки зрения использования ресурсов).