2012-06-28 139 views
15

目前我正在處理在其中一個例程中處理來自源目錄的文件的項目。有一個Java進程正在查找指定的目錄,並嘗試讀取和處理存在的文件。文件很大並且由其他第三方進程更新。問題是如何檢查文件是否完整寫入?我正在嘗試使用file.length(),但即使寫入過程尚未完成,它也會返回實際大小。我有一種感覺,解決方案應該取決於平臺。任何幫助,將不勝感激。檢查文件是否完全寫入

更新: 這個問題與重複實際沒有什麼不同,但它有一個工作代碼片段的答案,得到高度評價。

+3

您是否考慮過使用文件鎖? – cklab

+0

謝謝。檢查這個。我們能否確定鎖定總是通過寫入過程創建的? –

+2

好吧,這個概念是,如果你試圖獲得一個鎖,你應該得到一個例外。我剛剛遇到了這個:http://docs.oracle.com/javase/7/docs/api/java/io/File。html#canWrite%28%29從來沒有使用過它,但你可以試試看它是如何工作的。看起來很有趣。 – cklab

回答

9

生產者進程在寫完文件時是否關閉文件?如果是這樣,如果生產者進程仍在生產,試圖用排它鎖打開消費者進程中的文件將失敗。

+1

在linux下使用rsync命令複製文件,所以我相信它。好主意,現在就試試吧。 –

+0

你能寫一個例子來說明這個過程嗎? –

+0

這不是一個非常有用的正確答案:( –

2

我不認爲有一個通用的解決方案。尋找文件大小是錯誤的,因爲某些應用程序可以在任何寫入調用之前設置文件大小。其中一種可能性是使用鎖定。這將要求作者累積寫鎖(或獨佔鎖)。如果你不能修改編寫器,那麼你可以使用OS提供的工具,比如Linux上的fuser來查看是否存在仍然可以訪問文件的進程。

1

如果您打算在單一平臺上使用此代碼,您可以使用NIO's FileLock facility。但請仔細閱讀文檔,並注意在許多平臺上,鎖只是諮詢。

另一種方法是讓一個進程用您的進程無法識別的名稱寫入該文件,然後在寫入完成時將該文件重命名爲可識別的名稱。在大多數平臺上,如果源和目標是相同的文件系統卷,則重命名操作是原子操作。

+0

好的一點,謝謝,如果java沒有成功,就做到這一點,在某些情況下,改變生產者行爲有點困難 –

13

我得到了解決工作:

private boolean isCompletelyWritten(File file) { 
    RandomAccessFile stream = null; 
    try { 
     stream = new RandomAccessFile(file, "rw"); 
     return true; 
    } catch (Exception e) { 
     log.info("Skipping file " + file.getName() + " for this iteration due it's not completely written"); 
    } finally { 
     if (stream != null) { 
      try { 
       stream.close(); 
      } catch (IOException e) { 
       log.error("Exception during closing file " + file.getName()); 
      } 
     } 
    } 
    return false; 
} 

感謝@cklab和@Will和所有其他人誰提出的「排它鎖」的方式來看待。我只是在這裏發佈代碼以使其他感興趣的人使用它。我相信@tigran提出的重命名解決方案也可行,但純Java解決方案對我來說更可取。

P.S.最初我用FileOutputStream而不是RandomAccessFile,但它鎖定正在寫入的文件。

+2

這適用於Winows,但不適用於Linux – Nilesh

+0

@Nelish這很奇怪,因爲我最初在Linux for Linux上實現它 –

+0

嗯,我在UNIX上試了一下,winscp或ftp正在傳輸文件,而我試圖鎖定並且我能夠! – Nilesh

1

我已經在過去使用此方案與Windows一個簡單的解決方案是使用boolean File.renameTo(File)並嘗試將原始文件移動到一個單獨的臨時文件夾:

如果successfalse,那麼potentiallyIncompleteFile仍在寫入。

+0

Windows用戶的好建議,但它不是平臺不可知的。在Linux/Unix上,您可以刪除並重命名其他進程仍然打開的文件。 –

+1

這就是爲什麼我的答案說「與Windows」,而不是「所有系統」。 – JoshDM