当 jar 文件从 URL 作为 InputStream 打开时,JarEntry.getSize() 返回 -1

Posted

技术标签:

【中文标题】当 jar 文件从 URL 作为 InputStream 打开时,JarEntry.getSize() 返回 -1【英文标题】:JarEntry.getSize() is returning -1 when the jar files is opened as InputStream from URL 【发布时间】:2012-02-13 03:34:57 【问题描述】:

我正在尝试从 JarInputStream 读取文件并且文件大小返回 -1。我正在从 URL 作为 inputStream 访问 Jar 文件。

        URLConnection con = url.openConnection();

        JarInputStream jis = new JarInputStream(con.getInputStream());
        JarEntry je = null;

        while ((je = jis.getNextJarEntry()) != null) 
            htSizes.put(je.getName(), new Integer((int) je.getSize()));
            if (je.isDirectory()) 
                continue;
            
            int size = (int) je.getSize();
            // -1 means unknown size.
            if (size == -1) 
                size = ((Integer) htSizes.get(je.getName())).intValue();
            
            byte[] b = new byte[(int) size];
            int rb = 0;
            int chunk = 0;
            while (((int) size - rb) > 0) 
                chunk = jis.read(b, rb, (int) size - rb);
                if (chunk == -1) 
                    break;
                
                rb += chunk;
            
            // add to internal resource hashtable
            htJarContents.put(je.getName(), baos.toByteArray());
        

【问题讨论】:

【参考方案1】:

这是记录在案的行为。 Javadoc 说getSize() ...

“返回条目数据的未压缩大小,如果未知则返回 -1。”

显然,这是未压缩数据的实际大小未知的情况。您的应用程序只需要处理这个问题。


跟进

您评论了另一个答案:

虽然我们有总内容大小,但我认为如果没有单个文件大小,我们将无法读取文件。

如果您没有大小,您仍然可以读取文件并使用ByteArrayOutputStream 对其进行缓冲,然后调用toByteArray() 将缓冲的字节提取为byte[]。事实上,鉴于(根据 Tom Hawtin 的评论)报告的大小可能不正确,您可能无论如何都应该这样做,并将报告的大小视为提示......并将其用作ByteArrayOutputStream的初始容量。

(顺便说一句,您问题中的代码看起来可能是打算以这种方式工作的......从第 2 行到最后一行判断。问题是其余代码不使用 baos对象。)

【讨论】:

即使知道,也可能是谎言。 我正在尝试读取文件并缓冲它,现在我面临另一个问题。 JarEntry 的原始大小和缓冲大小不同,并且获得的字节数比预期的要多。我什至检查并确认 jar 文件没有损坏。 @ReshmaDonthireddy - 这就是 Tom Hawtin 说尺寸可能是谎言时的意思。【参考方案2】:
byte[] classbytes = null;
JarInputStream jis = new JarInputStream(is);
JarEntry je = null;
String jename = null;
while((je = jis.getNextJarEntry()) != null)
    jename = je.getName();
    if(je.getSize() != -1)
        classbytes = new byte[(int)je.getSize()];
        int len = (int) je.getSize();
        int offset = 0;
        while (offset != len)
            offset += jis.read(classbytes, offset, len - offset);
        classes.put(jename, classbytes);
     else 
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while(true)
            int qwe = jis.read();
            if(qwe == -1) break;
            baos.write(qwe);
        
        classbytes = baos.toByteArray();
        classes.put(jename, classbytes);
    

【讨论】:

这实际上回答了这个问题。当大小未知时,它将输入流读入一个临时的字节数组输出流,直到输入流结束,然后返回对应的字节数组。谢谢,这对我有用!【参考方案3】:

这可能是因为提供该 url 的服务器没有提供 contentLenght。这类似于您有时使用浏览器下载文件并显示 33Kb/??下载(相对于 33Kb/2049Kb 下载)。

要快速检查是否是这种情况,您可以执行以下操作:

URL url = new URL(yourUrl);
conn = url.openConnection();
size = conn.getContentLength();
if(size < 0) 
    System.out.println("Could not determine file size.");
 else 
    System.out.println(yourUrl + "\nSize: " + size);

【讨论】:

感谢安德烈·博德纳雷斯库。内容长度很好。我也可以访问文件名。问题在于找出单个文件的大小。我需要从输入流中读取这些文件并且必须放入一个哈希表。 如果内容长度返回正确的值,你不能用它来确定它(比如,在打开流之前)吗? 虽然我们有总内容大小,但我认为如果没有单独的文件大小,我们将无法读取文件。

以上是关于当 jar 文件从 URL 作为 InputStream 打开时,JarEntry.getSize() 返回 -1的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Java jar 文件中读取资源文件?

如何从 Java jar 文件中读取资源文件?

Katalon将外部jar作为依赖而不是作为项目资源?

当程序在 IntelliJ 中运行时,为啥我会收到 SSLHandshakeException 作为 JAR?

JAD 文件必须引用 JAR 文件

使用java从jar作为资源播放.wav文件