Java:BufferedInputStream的available()方法存在问题

Java:BufferedInputStream的available()方法存在问题,java,file-io,Java,File Io,我正在处理以下用于将大文件拆分为一组较小文件的代码: FileInputStream input = new FileInputStream(this.fileToSplit); BufferedInputStream iBuff = new BufferedInputStream(input); int i = 0; FileOutputStream output = new FileOutputStream(fileA

我正在处理以下用于将大文件拆分为一组较小文件的代码:

FileInputStream input = new FileInputStream(this.fileToSplit);
            BufferedInputStream iBuff = new BufferedInputStream(input);
            int i = 0;

            FileOutputStream output = new FileOutputStream(fileArr[i]);
            BufferedOutputStream oBuff = new BufferedOutputStream(output);

            int buffSize = 8192;
            byte[] buffer = new byte[buffSize];
            while (true) {
                if (iBuff.available() < buffSize) {
                    byte[] newBuff = new byte[iBuff.available()];
                    iBuff.read(newBuff);
                    oBuff.write(newBuff);
                    oBuff.flush();
                    oBuff.close();

                    break;
                }
                int r = iBuff.read(buffer);

                if (fileArr[i].length() >= this.partSize) {
                    oBuff.flush();
                    oBuff.close();
                    ++i;
                    output = new FileOutputStream(fileArr[i]);
                    oBuff = new BufferedOutputStream(output);
                }
                oBuff.write(buffer);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
FileInputStream input=newfileinputstream(this.fileToSplit);
BufferedInputStream iBuff=新的BufferedInputStream(输入);
int i=0;
FileOutputStream输出=新的FileOutputStream(fileArr[i]);
BufferedOutputStream oBuff=新的BufferedOutputStream(输出);
int buffSize=8192;
字节[]缓冲区=新字节[buffSize];
while(true){
if(iBuff.available()=此.partSize){
oBuff.flush();
oBuff.close();
++一,;
输出=新的FileOutputStream(fileArr[i]);
oBuff=新的缓冲输出流(输出);
}
对象写入(缓冲区);
}
}捕获(例外e){
e、 printStackTrace();
}
这是我看到的奇怪行为。。。当我使用3GB文件运行这段代码时,初始的iBuff.available()调用返回一个大约210000000的值,代码工作正常。当我在12GB文件上运行此代码时,初始的iBuff.available()调用只返回200000000的值(这比拆分文件大小500000000小,并导致处理出错)


我认为behvaior中的这种差异与32位windows上的这一事实有关。我将在4.5 GB和3.5 GB文件上运行更多测试。如果3.5文件有效,而4.5文件无效,这将进一步证实这是一个32位对64位的问题,因为4GB将是阈值。

如果您阅读javadoc,它会非常清楚地指出:

返回可以使用的字节数 可以从此输入流中读取 无阻塞(由我添加强调)

所以很明显,你想要的不是这个方法提供的。因此,根据底层的InputStream,您可能会更早地遇到问题(例如,通过网络的流,服务器不返回文件大小-您必须读取完整的文件并对其进行缓冲,才能返回“正确的”可用()计数,这将花费大量时间-如果您只想读取头怎么办?)

因此,正确的处理方法是更改解析方法,使其能够处理文件的各个部分。就我个人而言,我看不出有什么理由在这里使用available()-只要调用read()并在read()返回时立即停止-1就可以了。如果您想确保每个文件确实包含块大小字节,则可能会变得更复杂—如果这种情况很重要,只需添加一个内部循环即可

int blockSize = XXX;
byte[] buffer = new byte[blockSize];
int i = 0;
int read = in.read(buffer);
while(read != -1) {
   out[i++].write(buffer, 0, read);
   read = in.read(buffer);
} 

available并不是衡量还有多少数据需要读取,而是衡量在EOF或阻塞等待输入之前保证能够读取多少数据

在最后的决胜决胜局中,我们的胜负悬殊

     BufferedInputStream iBuff = new BufferedInputStream(input);
     int i = 0;

     FileOutputStream output;
     BufferedOutputStream oBuff=0;
     try{
        int buffSize = 8192;
        int offset=0;
        byte[] buffer = new byte[buffSize];
        while(true){
            int len = iBuff.read(buffer,offset,buffSize-offset);
            if(len==-1){//EOF write out last chunk
               oBuff.write(buffer,0,offset);
               break;
            }
            if(len+offset==buffSize){//end of buffer write out to file
               try{
                  output = new FileOutputStream(fileArr[i]);
                  oBuff = new BufferedOutputStream(output);
                  oBuff.write(buffer);
               }finally{
                  oBuff.close();
               }
               ++i;
               offset=0;
            }
            offset+=len;
        }//while
     }finally{
         iBuff.close();
     }

您根本不应该使用
InputStream.available()
函数。只有在非常特殊的情况下才需要


您也不应该创建大于1MB的字节数组。这是浪费记忆。普遍接受的方法是从源文件中读取一个小数据块(4KB到1MB),然后只存储目标文件中读取的字节数。直到到达源文件的末尾为止。

@ratchet和@Voo都是正确的。 至于发生了什么。 int最大值为2147483647()。 14千兆字节是15032385536,显然不适合整数。
请注意,根据API Javadoc()和@Voo所述,这根本不会破坏方法契约(只是这不是您想要的)

available()的正确用法很少,这不是其中之一。你不需要那么多垃圾。记住这一点:

int count;
byte[] buffer = new byte[8192]; // or more
while ((count = in.read(buffer)) > 0)
  out.write(buffer, 0, count);

这是Java中复制流的标准方法。

下面是一些分割文件的代码。如果性能对您至关重要,您可以试验缓冲区大小

package so6164853;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Formatter;

public class FileSplitter {

  private static String printf(String fmt, Object... args) {
    Formatter formatter = new Formatter();
    formatter.format(fmt, args);
    return formatter.out().toString();
  }

  /**
   * @param outputPattern see {@link Formatter}
   */
  public static void splitFile(String inputFilename, long fragmentSize, String outputPattern) throws IOException {
    InputStream input = new FileInputStream(inputFilename);
    try {
      byte[] buffer = new byte[65536];
      int outputFileNo = 0;
      OutputStream output = null;
      long writtenToOutput = 0;

      try {
        while (true) {
          int bytesToRead = buffer.length;
          if (bytesToRead > fragmentSize - writtenToOutput) {
            bytesToRead = (int) (fragmentSize - writtenToOutput);
          }

          int bytesRead = input.read(buffer, 0, bytesToRead);
          if (bytesRead != -1) {
            if (output == null) {
              String outputName = printf(outputPattern, outputFileNo);
              outputFileNo++;
              output = new FileOutputStream(outputName);
              writtenToOutput = 0;
            }
            output.write(buffer, 0, bytesRead);
            writtenToOutput += bytesRead;
          }
          if (output != null && (bytesRead == -1 || writtenToOutput == fragmentSize)) {
            output.close();
            output = null;
          }
          if (bytesRead == -1) {
            break;
          }
        }
      } finally {
        if (output != null) {
          output.close();
        }
      }
    } finally {
      input.close();
    }
  }

  public static void main(String[] args) throws IOException {
    splitFile("d:/backup.zip", 1440 << 10, "d:/backup.zip.part%04d");
  }
}
包so6164853;
导入java.io.FileInputStream;
导入java.io.FileOutputStream;
导入java.io.IOException;
导入java.io.InputStream;
导入java.io.OutputStream;
导入java.util.Formatter;
公共类文件剥离器{
私有静态字符串printf(字符串fmt、对象…args){
格式化程序格式化程序=新格式化程序();
formatter.format(fmt,args);
返回格式化程序.out().toString();
}
/**
*@param outputPattern请参见{@link Formatter}
*/
公共静态void splitFile(String inputFilename、long fragmentSize、String outputPattern)引发IOException{
InputStream输入=新文件InputStream(inputFilename);
试一试{
字节[]缓冲区=新字节[65536];
int outputFileNo=0;
OutputStream输出=null;
长写输出=0;
试一试{
while(true){
int bytesToRead=buffer.length;
if(bytesToRead>fragmentSize-WritentOutput){
bytesToRead=(int)(fragmentSize-writentooput);
}
int bytesRead=input.read(缓冲区,0,bytesToRead);
如果(字节读取!=-1){
if(输出==null){
字符串outputName=printf(outputPattern,outputFileNo);
outputFileNo++;
output=新文件OutputStream(outputName);
writentooput=0;
}
输出写入(缓冲区,0,字节读取);
writentooput+=字节读取;
}
if(输出!=null&&(bytesRead==-1 | | writentooput==fragmentS