【问题标题】:Java: Read from InputStream doesn't always read the same amount of dataJava:从 InputStream 读取并不总是读取相同数量的数据
【发布时间】:2011-12-14 01:17:24
【问题描述】:

无论好坏,我一直在使用如下代码,没有任何问题:

ZipFile aZipFile = new ZipFile(fileName);   
InputStream zipInput = aZipFile.getInputStream(name);  
int theSize = zipInput.available();  
byte[] content = new byte[theSize];  
zipInput.read(content, 0, theSize);

我用过(这个逻辑是获取可用大小并直接读取到字节缓冲区) 对于File I/O 没有任何问题,我也将它与 zip 文件一起使用。

但最近我遇到了一个案例,zipInput.read(content, 0, theSize); 实际上比可用的theSize 少了 3 个字节。

由于代码不在循环中检查zipInput.read(content, 0, theSize); 返回的长度,我读取了缺少最后 3 个字节的文件
后来程序无法正常运行(文件是二进制文件)。

对于更大尺寸的不同 zip 文件来说已经足够奇怪了,例如1075 字节(在我的情况下,有问题的 zip 条目是 867 字节)代码工作正常!

我知道代码的逻辑可能不是“最好的”,但为什么我现在突然遇到这个问题?

如果我立即使用较大的 zip 条目运行该程序,它会起作用吗?

非常欢迎任何意见

谢谢

【问题讨论】:

    标签: java jakarta-ee file-io zip inputstream


    【解决方案1】:

    来自InputStream read API 文档:

    尝试读取尽可能多的 len 个字节,但数量较小 可以阅读。

    ...和:

    返回:读入缓冲区的总字节数,如果是 -1 由于已到达流的末尾,因此没有更多数据。

    换句话说,除非 read 方法返回 -1,否则仍有更多数据可供读取,但您不能保证 read准确读取指定的字节数。指定的字节数是描述它将读取的最大数据量的上限

    【讨论】:

    • 我知道这一点。这就是为什么我提到我的方法不是那么好。尽管如此我试图理解这种行为,丢失了一个小文件中的最后 3 个字节但没有任何问题在较大的文件中
    • @user384706:理解特定行为没有任何目标:它依赖于实现,您的代码可能会以多种方式出错,具体取决于许多因素。了解您的代码通常会出错的原因以及如何修复它非常重要。
    【解决方案2】:

    使用available() 并不能保证它会将总可用字节数计入end of stream
    请参阅 Java InputStream's available() method。它说

    返回对可以从此输入流读取(或跳过)的字节数的估计值,而不会被下一次为此输入流的方法调用阻塞。下一次调用可能是同一个线程或另一个线程。单次读取或跳过这么多字节不会阻塞,但可能会读取或跳过更少的字节。

    请注意,虽然InputStream 的某些实现会返回流中的总字节数,但很多不会。使用此方法的返回值来分配旨在保存此流中所有数据的缓冲区是不正确的。

    您的问题的示例解决方案如下:

    ZipFile aZipFile = new ZipFile(fileName);   
    InputStream zipInput = aZipFile.getInputStream( caImport );  
    int available = zipInput.available();  
    byte[] contentBytes = new byte[ available ];  
    while ( available != 0 )   
    {   
        zipInput.read( contentBytes );   
        // here, do what ever you want  
        available = dis.available();  
    } // while available  
    ...   
    

    这肯定适用于所有大小的输入文件。

    【讨论】:

    • 请不要将available用于此!它没有帮助,只会让你的代码更脆弱。只需阅读,直到您没有更多内容为止。
    • @Joachim:那么available什么时候使用?
    • @user384706:知道的唯一有效用途是如果您想检查慢速流(例如套接字)以查看 if 有任何可用的数据(如果没有可用的数据,请执行其他操作)。它对其他任何事情都没有用的原因是它本质上是活泼的(即当您执行实际的 read() 时,这个数字可能已经改变了)。
    • @user384706:许多InputStream 实现都有一些“好的场景”,它们似乎可以工作。网络上的小文件、大文件、扇区对齐文件、MTU 大小。所有这些都可能是代码看起来不错的情况。一旦这些因素之一出错,您的代码就会突然产生错误的输出。
    • @user384706:这是一种危险的“便利”:available 的大小可能是一个与您实际需要无关的任意数字。如果您的目标是读取完整文件,那么您将需要更大的缓冲区。如果您的目标是尽可能快地读取,那么您的缓冲区应该是一些理智、恒定的大小以避免重新分配它,如果您的目标是读取“一个信息包”(在某些定义的协议中),那么您您需要确保您读取准确那么多字节。
    【解决方案3】:

    最好的方法如下:

    public static byte[] readZipFileToByteArray(ZipFile zipFile, ZipEntry entry)
        throws IOException {
        InputStream in = null;
        try {
            in = zipFile.getInputStream(entry);
            return IOUtils.toByteArray(in);
        } finally {
            IOUtils.closeQuietly(in);
        }
    }
    

    IOUtils.toByteArray(in) 方法一直读取直到 EOF 然后返回字节数组。

    【讨论】:

      猜你喜欢
      • 2010-11-18
      • 2012-10-20
      • 2021-07-02
      • 1970-01-01
      • 1970-01-01
      • 2013-04-16
      • 1970-01-01
      • 1970-01-01
      • 2014-06-13
      相关资源
      最近更新 更多