ZipInputStream.getNextEntry возвращает null для некоторых zip-файлов

у меня есть простой код для извлечения zip-файлов, он работал нормально, как и ожидалось, но во время моего теста я попробовал свой код с некоторыми zip-файлами (шрифтами, значками и шаблонами, которые я загрузил из интернета), чтобы убедиться, что он должен извлечь любые zip-файлы, но не работает с некоторыми zip-файлами, вот свернутый код для регенерации этой проблемы:

package com.test.mytest;

import java.io.FileInputStream;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

public class ZipExtractTest {

    public static final String ZIP_FILE = "/Users/XXXXX/Downloads/janne.zip";

    public static void main(String[]args) {
        unzipFile(ZIP_FILE);
        unzipStream(ZIP_FILE);
    }

    public static void unzipFile(String zipName) {
        try {

            ZipFile zf = new ZipFile(zipName);

            Enumeration ent = zf.entries();

            while(ent.hasMoreElements()) {
                System.out.println(ent.nextElement());
            }

        } catch(Exception e) {
            System.out.println(e);
        }
    }

    public static void unzipStream(String zipName) {
        try {
            ZipInputStream zis = new ZipInputStream(new FileInputStream(zipName));
            ZipEntry ze = zis.getNextEntry();

            if(ze == null) {
                System.out.println("unable to get first entry from zip file");
                zis.close();
                return;
            }

            while(ze != null) {
                System.out.println("Entry Found: " + ze);
                ze = zis.getNextEntry();
            }

            zis.closeEntry();
            zis.close();

        } catch(Exception e) {
            System.out.println(e);
        }
    }
}

на самом деле в моем реальном приложении я должен извлечь zip-файлы через inputstreams. В приведенном выше коде я пытаюсь выдержка " Янне.zip " я загрузил этот файл изhttp://www.iconian.com/fonts/janne.zip я могу извлечь его с помощью любого zip-инструмента и удивительно через метод" unzipFile(String zipName)", но с методом unzipStream(String zipName)

ZipEntry ze = zis.getNextEntry();

возвращает null

любая помощь будет оценили

3 ответов


не ответ на вопрос, почему этот конкретный файл не работает с java.util.zip, а если у вас есть возможность заменить использование java.util.zip.ZipInputStream С Apache commons-сжатие org.apache.commons.compress.archivers.zip.ZipArchiveInputStream (который должен быть API-совместимым), то я только что протестировал это в вашем файле примера, и он, похоже, работает успешно.

обычно я нахожу commons-compress гораздо более надежным, чем java.util.zip при распаковке файлов, созданных другими инструментами, кроме java.util.zip классы сами себя.

Edit: Я сделал немного отладки в Eclipse, и похоже, что этот конкретный zip-файл имеет один сегмент, охватывающий маркер of 0x30304b50 перед подписью LOC (0x04034b50) локального заголовка первой записи. Это то, что commons-compress знает, как обращаться но java.util.zip Не - если j.u.z.ZipInputStream видит что-нибудь, кроме подписи LOC тогда getNextEntry() вернутся null.


смешно!

Я отлаживать свой код и получил ту же ошибку. Я нашел проверку заголовка в реализации ZipInputStream, но не в реализации ZipFile.

Не спрашивайте меня, почему, но заголовок в ваш zip-файл не действительным!

Your file is starting with: 50 4B 30 30 50 4B 03 04
A valid Zip File Header is: 50 4B 03 04

Если вы удалите первые байты (50 4B 30 30) из вашего файла вы получили действительный заголовок, и вы можете прочитать файл!



У меня была такая же проблема ! К счастью для меня, я смог решить эту проблему.
сначала я сбросил данные blob в базе данных, затем использовал java-код для его zip с помощью ZipInputStream. Хотя я не уверен, проблема null ZipEntry может быть из-за 2 вещей:
1. Данные blob в базе данных хранятся неправильно (или могут быть уже сжаты, некоторые базы данных сжимают данные blob во время хранения. вы можете тоже погуглить).
2. Потоки вход-выхода могут также вызывают проблемы, см. этой


вот подробное описание того, что я сделал:
1. сбросьте поле blob в базе данных с помощью EMPTY_BLOB и зафиксируйте изменения
2. используйте приведенную ниже программу java для обновления поля blob с помощью a .xls файл

DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver ()); // register driver

Connection conn =
   DriverManager.getConnection ("jdbc:oracle:thin:@my-local-database:1521:test", "test1", "test1");

// It's faster when auto commit is off: 
conn.setAutoCommit (false);

try
{
      PreparedStatement pstmt = conn.prepareStatement("update content set file_content = ? where CONTENT_ID=2006");
      File blob = new File("C:/Users/ankur/Desktop/Book1.xls");
      FileInputStream in = new FileInputStream(blob);

      pstmt.setBinaryStream(1, in); 
      pstmt.executeUpdate();
      conn.commit();
      conn.close();
      System.out.println("file updated");
}
catch (SQLException e)
{
   e.printStackTrace();
}

обратите внимание, что приведенный выше код будет работать, но он абсолютно не демонстрирует стандарты и практику кодирования.
3. Используется метод zip ниже для сжатия данные

public byte[] zipByteArray(String primaryKey, byte[] input) throws IOException{
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ZipOutputStream zos = new ZipOutputStream(baos);
    ZipEntry entry = new ZipEntry(primaryKey);
    entry.setSize(input.length);
    zos.putNextEntry(entry);
    zos.write(input);
    zos.closeEntry();
    zos.close();
    return baos.toByteArray();
}

вышеуказанный метод берет массив байтов, сжимает его, помещает его в ByteArrayOutputStream. Вы можете использовать сам ByteArrayOutputStream, из-за некоторых требований я преобразую его в массив байтов.
4. Затем я вставляю вышеуказанный массив байтов в поле blob, используя подготовленный оператор
5. Если я использую код распаковки, приведенный ниже, он работает нормально!

public byte[] unzipInputStream(InputStream is) throws IOException {
    ByteArrayOutputStream byteArrayOutputStream = null;
    ZipInputStream zipIs = new ZipInputStream(new BufferedInputStream(is));
    byteArrayOutputStream = new ByteArrayOutputStream();
    ZipEntry entry = zipIs.getNextEntry();
    while (entry != null) {
        byte[] tmp = new byte[2048];
        BufferedOutputStream bos = null;
        bos = new BufferedOutputStream(byteArrayOutputStream);
        int size = 0;
        while ((size = zipIs.read(tmp)) != -1) {
            bos.write(tmp, 0, size);
        }
        bos.flush();
        bos.close();
        entry = zipIs.getNextEntry();
    }
    zipIs.close();
    return byteArrayOutputStream.toByteArray();

выход вышеуказанного метода-это распакованные данные.