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

ZipEntry ze = zis.getNextEntry();

Возвращает null

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

3 9

3 ответа:

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

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

Edit: у меня проделал небольшую отладку в Eclipse, и похоже, что этот конкретный zip-файл имеет односегментный охватывающий маркер 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-код, чтобы заархивировать его с помощью 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();

Выводом вышеприведенного метода являются распакованные данные.