2012-04-05 26 views
22

比方說你有一個外部進程寫入文件到某個目錄,你有一個單獨的過程中定期嘗試從這個目錄中讀取文件。要避免的問題是讀取另一個進程當前正在寫入的文件,因此它將不完整。目前,讀取過程使用最小文件時間定時器檢查,因此它會忽略所有文件,除非它們的最後修改日期超過了XX秒。如何測試一個文件是「完全」(完全寫入)與Java

我想知道是否有更清晰的方法來解決這個問題。如果文件類型是未知的(可能是多種不同的格式),是否有一些可靠的方法來檢查文件頭中應該在文件中的字節數與當前在文件中確認它們匹配的字節數?

感謝您的任何想法或想法!

+1

你有沒有在過程中寫入文件到你的目錄中的任何控制觀看? – 2012-04-05 13:20:32

+0

除了在完成後重命名文件之外,我所採取的方法是在寫入文件時讀取該文件(在Unix中認爲是「tail」) – 2012-04-05 13:42:21

回答

9

您可以使用外部標記文件。寫入過程可以在開始創建文件XYZ之前創建文件XYZ.lock,並在XYZ完成後刪除XYZ.lock。讀者可以很容易地知道,只有當相應的.lock文件不存在時,它纔可以認爲文件是完整的。這似乎解決了這個問題

+0

嗨Michal,我們如何檢查文件是否被鎖定「通過計劃。 – 2015-01-13 06:02:10

+0

在這裏,文件上沒有額外的鎖 - 文件存在或不存在是鎖構成的事實。 – 2015-01-13 13:01:01

+1

如果您無法控制編寫器進程,該怎麼辦? – Matthieu 2016-05-07 13:27:09

2

即使字節數相等,文件內容也可能不同。

所以我認爲,你必須逐字節匹配舊文件和新文件。

1

2個選擇:

  1. 最佳選項 - 寫進程通知讀取過程在某種程度上是 書寫結束了。
  2. 將文件寫入{id} .tmp,而不是完成時將其重命名爲{id} .java,並且讀取過程僅在* .java文件中運行。重命名花費的時間少得多,這兩個過程一起工作的機會減少。
1

首先,有Why doesn't OS X lock files like windows does when copying to a Samba share?但這是你已經做的變化。

至於讀取任意文件和尋找大小,有些文件有一個信息,有的沒有,但即使是那些不具有代表它的任何常見的方式。您需要每種格式的特定信息,並分別獨立管理它們。

如果你絕對必須的「即時」,它的完成文件的行爲,那麼你的寫作過程中需要發送某種形式的通知。否則,你幾乎堅持輪詢文件,並且與從隨機文件中讀取隨機塊相比,讀取目錄在I/O方面相當便宜。

8

我已經在過去這樣做的方式是,這一進程寫入文件寫入到一個「臨時」文件,然後當它已經完成寫入文件的文件移動到讀出的位置。

所以寫作過程中會寫信給info.txt.tmp。完成後,它將文件重命名爲info.txt。然後讀取過程只需檢查是否存在info.txt - 它知道如果它存在,它已被完全寫入。

或者你可以有寫過程寫信息。txt到不同的目錄,如果你不喜歡使用奇怪的文件擴展名,將它移動到讀取目錄。

2

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

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

2

當客戶端通過密鑰對SFTP上傳文件時,我沒有選擇使用臨時標記等。它們可能非常大。

它很hacky,但我比較睡眠前後幾秒的文件大小。

它顯然不是理想的鎖的線程,但是在我們的情況下,它僅僅是作爲一個後臺系統進程運行,這樣似乎做工精細

private boolean isCompletelyWritten(File file) throws InterruptedException{ 
    Long fileSizeBefore = file.length(); 
    Thread.sleep(3000); 
    Long fileSizeAfter = file.length(); 

    System.out.println("comparing file size " + fileSizeBefore + " with " + fileSizeAfter); 

    if (fileSizeBefore.equals(fileSizeAfter)) { 
     return true; 
    } 
    return false; 
} 

注:如下面提到這可能不是在Windows上運行。這在Linux環境中使用。

+0

只有故障點會是網絡崩潰 – Skynet 2017-04-11 11:39:56

+0

由於文件大小元數據是作爲Windows中的第一步寫入的,因此此代碼將失敗。所以總是file.length()是相同的 – debugger89 2017-07-24 08:20:05

0

這可能通過使用Apache Commons IO maven庫FileUtils.copyFile()方法來實現。如果您嘗試複製文件並獲取IOException,則意味着該文件未完全保存。

例子:

public static void copyAndDeleteFile(File file, String destinationFile) { 

    try { 
     FileUtils.copyFile(file, new File(fileDirectory)); 
    } catch (IOException e) { 
     e.printStackTrace(); 
     copyAndDeleteFile(file, fileDirectory, delayThreadPeriod); 
    } 

或定期用文件夾中的一些延遲大小包含此文件檢查:

FileUtils.sizeOfDirectory(folder); 
+0

Commons IO能夠跟蹤這一點很有趣。所以這可能會回答原來的問題,而沒有複雜的複製。 – Thomas 2017-12-15 13:02:43