2015-03-31 19 views
0

我在某個問題上停留了一段時間,我不知道該怎麼做。java中的大文件讀取不一致問題

目的是將文件上傳到Google Cloud Storage,但由於它是一個大文件,而且我想要一些效率,所以我使用一個線程來讀取它,並且我以2Mo的塊爲單位預先分割該文件。這些塊存儲在一個小隊列中(大約2到5個插槽),並且可以由我的上傳者類(進行PUT請求的人)訪問。

但是(因爲總是有一個)塊在每臺計算機上都不一致。我嘗試了很多事情,BufferedInputStream,PushBackInputStream,FileChannel(有或沒有MappedByteBuffer)都沒有什麼可做,受到影響的計算機在閱讀期間失敗,最後一部分(比正常塊小)比預期的要大(所以總數讀取的字節數超過原始計算的文件大小)。

我不知道爲什麼,但在某些計算機上(顯着數量),看起來文件在閱讀期間增長。 我錯過了什麼嗎?我究竟做錯了什麼 ?我可以截斷剩餘的字節嗎?但如果突然小於預期,該怎麼辦? 我出來的想法,所以我請求你的:)

哦,一點點技巧,由於在上傳過程中恢復能力,我必須能夠回到我的閱讀,所以這是減少我的班級數量使用(標記支持,或fileChannel情況下的位置)。

如果您對CPU和內存優化任何意見,歡迎您太:)(這裏沒有這一切,但其餘它只是BlockingQueue的實現與Q)

這裏是我的讀者的過去: http://paste.awesom.eu/Teraglehn/pw09&ln

而且有趣的部分:

public void run() { 
    try { 
     byte[] chunk = new byte[chunkSize]; 
     int read; 
     int r; 
     long skipped; 
     while (!shouldStop && !finishReading && !stopped) { 
      if(size()>=maxSize){ 
       continue; 
      } 
      read = 0; 
      System.out.println("[available1] "+available); 
      System.out.println("[available2] "+inputStream.available()); 
      if(pendingFix !=0){ 
       System.out.println(String.format("Fix of %d bytes asked", pendingFix)); 
       clear(); 
       if (pendingFix > 0) { 
        pendingFix = Math.min(pendingFix, (int) available); 
        skipped = inputStream.skip((long) pendingFix); 
        if(skipped != pendingFix){ 
         throw new IOException(String.format("Ask fix of %d bytes has not been completely done (%d bytes actually skipped for unknown reason)", pendingFix, skipped)); 
        } 
        incrementCursor(pendingFix); 
       }else { 
        decrementCursor(Math.min(cursor, -pendingFix)); 
        inputStream.reset(); 
        skipped = inputStream.skip(cursor); 
        if(skipped != cursor){ 
         throw new IOException(String.format("Ask fix of %d bytes has not been completely done (%d bytes actually back skipped for unknown reason)", pendingFix, cursor-skipped)); 
        } 
       } 
       pendingFix = 0; 
      } 
      while(read < chunkSize){ 
       r = inputStream.read(chunk, read, chunkSize-read); 
       if(r<0) { 
        read = (read > 0)? read : r; 
        break; 
       } 
       else { 
        read +=r; 
       } 
      } 

      if(pendingFix!=0) continue; 
      if(read != chunkSize){ // Probably end of file 
       if(read == -1){ 
        finishReading = true; 
       }else if(available == read){ 
        System.out.println("Partial chunk (end)"); 
        incrementCursor(read); 
        put(Arrays.copyOfRange(chunk, 0, read)); 
        finishReading = true; 
       }else { 
        throw new IOException(String.format("Only %d bytes have been read on %d bytes asked for unknown reason, %d bytes available", read, chunkSize, available)); 
       } 
      }else { 
       System.out.println("Full chunk (running)"); 
       put(chunk.clone()); 
       incrementCursor(read); 
      } 
     } 
    }catch(IOException e){ 
     this.interrupt(); 
     errors.add(e); 
     e.printStackTrace(); 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
    stopped = true; 
} 

PS:有一些樂趣,這一切,我通過會議上傳會話,會話與一個或多個大型文件的文件夾,它總是ŧ他最後一個失敗的文件....

+0

這一切都太複雜了。你需要知道的基本事情是read()沒有義務填充緩衝區。 – EJP 2015-03-31 09:10:40

+0

我知道,這就是爲什麼有一段時間。但爲什麼它閱讀更多的應該保持閱讀文件?這就是問題所在 ! PS:爲什麼只有一些電腦?我甚至嘗試在受影響的PC上的IDE中運行讀卡器,使用相同的文件上傳,並且它完美地工作... – 2015-03-31 09:17:46

回答

1

當所有的事情都以某種方式發生時,你已經創造了一個混亂的局面,但如果某件事情沒有達到你期望的程度,它就會失敗。您使用的是available(),這很可能是錯誤的或至少是無用的。

您的讀取循環也是錯誤的,因爲它正在填充chunk數組,但假設每次讀取都完全填滿(如果沒有,則先前的字節會被覆蓋)。

你的// Probably end of file評論意味着你有一個邏輯問題。所以我建議用簡單的英文寫出邏輯,然後重寫代碼。

+0

天哪,對於閱讀而抱歉,我編輯了它。 但是對於可用的()我只在日誌中使用它來查看輸入流中發生了什麼,我知道它不能保證返回剩餘的實際大小。 而對於評論...... mmh我真的不知道爲什麼它在這裏其實,我可以只寫「文件結束」。但是InputStream沒有指定它應該讀取的內容少於預期,所以可能會出現這種情況,它不是文件的結尾...... – 2015-03-31 09:54:03

+0

在EOF讀取時'InputStream'返回-1。至於'available()',它返回可以不阻塞地讀取的字節數量。這很少有用。簡化您的邏輯,並且可以更輕鬆地查看出現問題的地方。 – Kayaman 2015-03-31 10:28:32

+0

我會盡力的。不,我會的。 – 2015-03-31 11:43:55