【问题标题】:Unzipping into a ByteArrayOutputStream -- why am I getting an EOFException?解压到 ByteArrayOutputStream —— 为什么我会收到 EOFException?
【发布时间】:2019-12-12 22:51:58
【问题描述】:

我一直在尝试创建一个 Java 程序,该程序将从在线 API 读取 zip 文件,将它们解压缩到内存中(而不是文件系统中),然后将它们加载到数据库中。 由于解压后的文件需要按特定顺序加载到数据库中,因此我必须先解压所有文件,然后才能加载其中的任何一个。

我基本上使用 StackOverflow 上的 another question 作为如何做到这一点的模型。使用来自util.zipZipInputStream 我可以使用较小的 ZIP(压缩后 0.7MB ~ 解压缩 4MB)来执行此操作,但是当我遇到更大的文件(压缩后 25MB,解压缩时 135MB)时,两个最大的文件没有被读入记忆。我什至无法为这些较大的文件(8MB 和 120MB,后者构成 zip 文件中的绝大多数数据)检索 ZipEntry。没有抛出异常,我的程序继续运行,直到它尝试访问无法写入的解压缩文件并抛出 NullPointerException。

我正在使用 Jsoup 从网上获取 zipfile。

有没有人有这方面的经验并且可以就我为什么无法检索 zip 文件的完整内容提供指导?

以下是我正在使用的代码。我在 HashMap 中以InputStreams 的形式收集解压缩文件,当没有更多的ZipEntrys 时,程序应该停止寻找ZipEntrys 的剩余空间。

    private Map<String, InputStream> unzip(ZipInputStream verZip) throws IOException {

        Map<String, InputStream> result = new HashMap<>();

        while (true) {
            ZipEntry entry;
            byte[] b = new byte[1024];
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int l;

            entry = verZip.getNextEntry();//Might throw IOException

            if (entry == null) {
                break;
            }

            try {
                while ((l = verZip.read(b)) > 0) {
                    out.write(b, 0, l);
                }
                out.flush();
            }catch(EOFException e){
                e.printStackTrace();
            }
            catch (IOException i) {
                System.out.println("there was an ioexception");
                i.printStackTrace();
                fail();
            }
            result.put(entry.getName(), new ByteArrayInputStream(out.toByteArray()));
        }
        return result;
    }

如果我的程序利用文件系统解压缩文件,我会不会更好?

【问题讨论】:

  • 您不应该尝试将所有这些数据保存在内存中。当需要写入数据库时​​,只需通过ZipEntrys,而不是尝试将它们全部放在 Map 中。此外,您需要while ((l = verZip.read(b)) &gt;= 0)——注意&gt;= 而不是&gt;。否则,您的代码将在第一次遇到零字节时停止读取数据。
  • @VGR 我编辑了帖子以回答这个问题。解压后的文件需要按特定顺序加载到数据库中,所以我必须先解压所有文件,然后再加载。
  • @VGR 您对读取循环的评论不正确。他正在读入一个缓冲区,返回值是一个计数,而不是一个字节值。这段代码永远不会返回零,除非b[] 的长度为零。
  • 当你忽略它们时,你怎么知道没有抛出异常?
  • 所需的插入顺序与 zip 文件中的条目顺序是否不同?在我看来,通过将名称存储在设计上无序的 HashMap 中,您会失去任何排序。

标签: java memory zip jsoup zipinputstream


【解决方案1】:

事实证明,Jsoup 是问题的根源。使用 Jsoup 连接获取二进制数据时,从连接中读取的字节数是有限制的。默认情况下,此限制为 1048576,即 1 兆字节。结果,当我将 Jsoup 中的二进制数据输入ZipInputStream 时,生成的数据在 1 兆字节后被截断。这个限制,maxBodySizeBytes 可以在org.jsoup.helper.HttpConnection.Request 中找到。

        Connection c = Jsoup.connect("example.com/download").ignoreContentType(true);
        //^^returns a Connection that will only retrieve 1MB of data
        InputStream oneMb = c.execute().bodyStream();
        ZipInputStream oneMbZip = new ZipInputStream(oneMb);

试图解压缩截断的oneMbZip 是导致我得到EOFException 的原因

使用下面的代码,我能够将Connection 的字节限制更改为 1 GB (1073741824),然后能够在不遇到EOFException 的情况下检索 zip 文件。

        Connection c = Jsoup.connect("example.com/download").ignoreContentType(true);
        //^^returns a Connection that will only retrieve 1MB of data
        Connection.Request theRequest = c.request();
        theRequest.maxBodySize(1073741824);
        c.request(theRequest);//Now this connection will retrieve as much as 1GB of data
        InputStream oneGb = c.execute().bodyStream();
        ZipInputStream oneGbZip = new ZipInputStream(oneGb);

请注意,maxBodySizeBytes 是一个 int,其上限为 2,147,483,647,或略低于 2GB。

【讨论】:

    猜你喜欢
    • 2020-09-14
    • 1970-01-01
    • 2023-04-05
    • 2016-08-21
    • 2014-07-04
    • 2020-06-06
    • 2018-12-26
    • 2021-03-05
    • 2013-07-13
    相关资源
    最近更新 更多