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 ответа:
Не ответ на вопрос, почему этот конкретный файл не работает с
Обычно я нахожу commons-compress гораздо более надежным, чемjava.util.zip
, но если у вас есть возможность заменить использованиеjava.util.zip.ZipInputStream
на Apache commons-compressorg.apache.commons.compress.archivers.zip.ZipArchiveInputStream
(который должен быть совместим с API), то я только что проверил это на вашем примере файла, и это, кажется, работает успешно.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();
Выводом вышеприведенного метода являются распакованные данные.