【问题标题】:ZipInputStream.getNextEntry returns null on some zip filesZipInputStream.getNextEntry 在某些 zip 文件上返回 null
【发布时间】:2013-03-09 10:53:54
【问题描述】:

我有一个简单的代码来提取 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 文件。在上面的代码中,我试图提取“janne.zip”,我从http://www.iconian.com/fonts/janne.zip 下载了这个文件(String zipName) 方法

ZipEntry ze = zis.getNextEntry();

返回空值

任何帮助将不胜感激

【问题讨论】:

标签: java unzip zipfile compression


【解决方案1】:

不是关于为什么这个特定文件不能与 java.util.zip 一起使用的答案,但是如果您可以选择将您对 java.util.zip.ZipInputStream 的使用替换为 Apache commons-compress org.apache.commons.compress.archivers.zip.ZipArchiveInputStream(应该与 API 兼容) ) 然后我刚刚在您的示例文件上进行了测试,它似乎可以成功运行。

一般来说,在解压由java.util.zip 类本身以外的工具创建的文件时,我发现 commons-compress 比 java.util.zip 可靠得多。

编辑:我在 Eclipse 中做了一些调试,看起来这个特定的 zip 文件在 LOC 签名 (0x04034b50) 之前有一个 single segment spanning marker 或第一个条目的本地标头。这是 commons-compress knows how to handlejava.util.zip 不会 - 如果 j.u.z.ZipInputStream 看到除 LOC 签名之外的任何内容,则 getNextEntry() 将返回 null

【讨论】:

  • 谢谢罗伯茨,这很奇怪。我想我会接受你的建议,并且肯定会尝试 commons-compress API,因为我正在寻找一些可靠的解压缩 API。再次感谢!
  • 很高兴您回来回答最初的问题,+1 为我节省了几个小时的搜索时间。
【解决方案2】:

好笑!

我调试了您的代码并得到了同样的错误。我在 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),您将获得一个有效的标题,您可以读取您的文件!

【讨论】:

  • 确实很有趣!问题是我必须将此实现交付给根本不知道文件标题之类的东西的人。他就像如果我能够使用标准工具提取一个 zip 文件,那么为什么不通过你的代码。无论如何谢谢 - 即使有这样的标题,我也可以通过 Apache commons-compress 提取它
【解决方案3】:


我遇到了同样的问题!幸运的是我能够解决它。
首先我重置数据库中的 blob 数据,然后使用 java 代码使用 ZipInputStream 对其进行压缩。虽然我不确定,但空 ZipEntry 问题可能是因为 2 件事:
1.数据库中的blob数据存储不正确(或者可能已经压缩了,有些数据库在存储的时候会压缩blob数据,你也可以google一下)。
2. 输入/输出流也会造成麻烦,见this


以下是我所做的详细说明:
1. 使用 EMPTY_BLOB 重置数据库中的 blob 字段并提交更改
2.使用下面的java程序用.xls文件更新blob字段

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();

上述方法的输出是解压后的数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多