2011-05-28 80 views
2
可用()方法

我處理下面的代碼被用於一個大文件分割成一組較小的文件:的Java:問題與的BufferedInputStream

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

這是怪異的行爲我看到...當我使用3GB文件運行此代碼時,最初的iBuff.available()調用返回大約21,000,000個值,代碼正常工作。當我在12GB文件上運行此代碼時,最初的iBuff.available()調用僅返回200,000,000的值(這比分割文件大小500,000,000小,並導致處理出錯)。

我在想,這種差異在behvaior中與事實有關,這是在32位窗口上。我將在4.5 GB文件和3.5 GB文件上運行更多測試。如果3.5文件起作用,而4.5文件不起作用,那麼這將進一步證實它是32位與64位問題的理論,因爲4GB將成爲閾值。

+2

大約「2,100,000,000」?我敢打賭它是'2,147,483,647 = 2^31 - 1'。 Java中int的最大值。 '2^32'字節大約是'4.29 GB'。 – Ishtar 2011-05-28 23:14:48

回答

7

那麼,如果你閱讀的javadoc它相當清楚地指出:

返回可 從此輸入流中讀取的字節數 而不阻塞(重點由我加的)

所以很明顯,你想要的不是這個方法提供的。因此,根據底層的InputStream,你可能會遇到很多問題(例如,網絡上的流與不返回文件大小的服務器 - 你必須讀取完整的文件並緩衝它,以便返回「正確的」可用的()計數,這會花費很多時間 - 如果你只想讀一個頭文件怎麼辦?)

所以正確的處理方法是改變你的解析方法,以便能夠處理文件。就我個人而言,我甚至都沒有看到甚至在這裏使用available()的原因 - 只要調用read()並在read()返回-1時立即停止應該可以正常工作。如果你想確保每個文件都包含blockSize字節,那麼可以使其更加複雜 - 如果該場景很重要,只需添加一個內部循環。

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); 
} 
0

可用的不是多少還是要讀,但措施更多的是衡量有多少是保證能夠它可能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(); 
    } 
+1

查看其他答案。太複雜了。 – EJP 2011-05-29 01:01:00

2

根本不應該使用InputStream.available()函數。只有在非常特殊的情況下才需要。您也不應創建大於1 MB的字節數組。這是對記憶的浪費。普遍接受的方法是從源文件中讀取一個小塊(4 kB到1 MB),然後僅在目標文件中讀取的許多字節,然後存儲。直到你到達源文件的末尾爲止。

+0

那裏有體系結構(包括x86),頁面大小> 1MB,在這種情況下,它可能是有用的。雖然如果性能如此重要,那麼使用我所假設的NIO通道可能會更有意義。 – Voo 2011-05-28 23:15:34

+0

我將取出可用()調用,但再次查看代碼,您將看到正在創建的最大字節數組爲8192. – opike 2011-05-28 23:33:57

+0

是的,在大多數情況下,您是正確的。 (有可能第二次調用'available'會返回比第一次調用更大的值;)) – 2011-05-28 23:40:18

4

available()的正確用法很少,這不是其中之一。你不需要那些垃圾。記住這一點:

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

這是用Java複製流的規範方法。

0

下面是一些分割文件的代碼。如果性能對您至關重要,您可以嘗試使用緩衝區大小。

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"); 
    } 
} 

一些言論:這實際上已經從輸入文件讀取

  • 只有那些字節寫入到輸出文件中的一個。
  • 我遺漏了BufferedInputStreamBufferedOutputStream,因爲它們的緩衝區大小隻有8192字節,比我在代碼中使用的緩衝區小。
  • 只要我打開一個文件,無論發生什麼事情,我都要確保它在最後關閉。 (finally塊。)
  • 該代碼只包含一個調用input.read和只有一個調用output.write。這使得更容易檢查正確性。
  • 用於分割文件的代碼不能捕獲IOException,因爲它不知道在這種情況下該做什麼。它只是傳遞給調用者;也許來電者知道如何處理它。