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();
выход вышеуказанного метода-это распакованные данные.