2013-12-09 72 views
3

我正在研究應該有效(並遞歸)將文件/目錄從源位置複製到目標位置的Java過程。在Java中使用文件鎖複製文件

要做到這一點,我想:

  • 創建鎖
  • 如果目標文件不存在,複製
  • 否則如果目標文件不同的是,將它複製
  • 否則它們是一樣的,所以什麼都不做
  • 釋放鎖

要檢查內容是否相同,我正計劃使用Apache Commons IO FileUtils方法contentsEqual(...)。爲了進行復制,我打算使用Apache Commons IO FileUtils方法copyFile(...)

所以,我想出了代碼是(這僅僅是文件,目錄遞歸向下處理此方法的文件):

private static void checkAndUpdateFile(File src, File dest) throws IOException { 
    FileOutputStream out = new FileOutputStream(dest); 
    FileChannel channel = out.getChannel(); 
    FileLock lock = channel.lock(); 

    if (!dest.exists()) { 
    FileUtils.copyFile(src, out); 
    } else if (!FileUtils.contentEquals(src, dest)) { 
    FileUtils.copyFile(src, out); 
    } 

    lock.release(); 
    channel.close(); 
    out.close(); 
} 

這確實的文件鎖定(偉大的),並複製文件(超級)。

但是,每當它複製文件時,它都會將複製文件的上次修改時間戳設置爲複製時間。這意味着後續調用FileUtils.contentEquals(src, dest)繼續返回false,因此文件被重新複製。

我真的很喜歡類似於FileUtils.copyFile(src, dest, true),它保留了文件時間戳 - 並且從調用FileUtils.contentEquals(src, dest)變爲true。這將要求鎖定在File上,而不是FileOutputStream,否則將調用FileUtils.copyFile(src, dest, true)失敗並拋出異常,因爲文件被鎖定。

或者,我考慮做FileUtils.copyFile(src, dest, true)方法所做的事,即調用dest.setLastModified(src.lastModified())。但是,這將不得不在鎖釋放後調用,如果同一進程已被同時執行超過一次,則可能會導致問題。

我也考慮過把鎖放在源文件上的ide,但是這並沒有幫助,因爲我不得不把它放在FileInputStream上,我想通過FileFileUtils.copyFile(src, dest)

所以:

  1. 有沒有更簡單的方法來實現什麼,我試圖做的?
  2. 是否可以鎖定文件而不是文件的派生?
  3. 解決此問題的最佳方法是什麼?
    • 即我只需要編寫我自己的方法來處理這部分問題? (可能copyFile(...)

回答

0

所以......最後,我去寫我自己的copy()compare()方法使用已鎖定FileChannel對象的方法。以下是我提出的解決方案 - 儘管我希望其他人可能會提出改進建議。這是通過查看apache.commons.io類的源代碼FileUtilsIOUtils得到的。

private static final int s_eof = -1; 
private static final int s_byteBuffer = 10240; 

private static void checkAndUpdateFile(File src, File dest) throws IOException { 
    FileInputStream in = new FileInputStream(src); 
    FileChannel srcChannel = in.getChannel(); 
    FileChannel destChannel = null; 
    FileLock destLock = null; 

    try { 
    if (!dest.exists()) { 
     final RandomAccessFile destFile = new RandomAccessFile(dest, "rw"); 
     destChannel = destFile.getChannel(); 
     destLock = destChannel.lock(); 
     copyFileChannels(srcChannel, destChannel); 
     dest.setLastModified(src.lastModified()); 
    } else { 
     final RandomAccessFile destFile = new RandomAccessFile(dest, "rw"); 
     destChannel = destFile.getChannel(); 
     destLock = destChannel.lock(); 
     if (!compareFileChannels(srcChannel, destChannel)) { 
     copyFileChannels(srcChannel, destChannel); 
     dest.setLastModified(src.lastModified()); 
     } 
    } 
    } finally { 
    if (destLock != null) { 
     destLock.release(); 
    } 
    if (destChannel != null) { 
     destChannel.close(); 
    } 
    srcChannel.close(); 
    in.close(); 
    } 
} 

protected static void copyFileChannels(FileChannel src, 
             FileChannel dest) throws IOException { 
    final long size = src.size(); 
    for (long pos = 0; pos < size;) { 
    long count = 
     ((size - pos) > s_byteBuffer) ? s_byteBuffer : (size - pos); 
    pos += dest.transferFrom(src, pos, count); 
    } 
} 

protected static boolean compareFileChannels(FileChannel a, 
              FileChannel b) throws IOException { 
    if (a.size() != b.size()) { 
    return false; 
    } else { 
    final ByteBuffer aBuffer = ByteBuffer.allocate(s_byteBuffer); 
    final ByteBuffer bBuffer = ByteBuffer.allocate(s_byteBuffer); 
    for (int aCh = a.read(aBuffer); s_eof != aCh;) { 
     int bCh = b.read(bBuffer); 
     if (aCh != bCh || aBuffer.compareTo(bBuffer) != 0) { 
     return false; 
     } 
     aBuffer.clear(); 
     aCh = a.read(aBuffer); 
     bBuffer.clear(); 
    } 
    return s_eof == b.read(bBuffer); 
    } 
} 
0

您可以使用Guava Google Core Library來比較兩個文件。

As Byte Source

ByteSource Doc

 ByteSource inByte = Resources.asByteSource(srcFileURL); 
     ByteSource outByte = Files.asByteSource(srcFileURL2); 
     boolean fileEquals= inByte.contentEquals(outByte)); 
+0

這是一個不錯的主意,但如何與文件鎖定一起工作?我無法在'ByteSource'上獲得'FileLock',我無法從一個文件創建一個ByteSource,同時存在一個單獨的鎖,並且將'ByteSource'放在鎖之外是沒有意義的 - 也就是說,在問題上,這在功能上等同於使用'apache.commons.io.FileUtils' – amaidment

+0

@amaidment,您可以從文件http://docs.oracle.com/javase/7/docs/api/java/io/File.html獲取URL #toURL%28%29。一旦你鎖定文件獲取URL並獲得輸入字節源和目標文件相同 – Makky

+0

不,這是行不通的。當代碼調用'inByte.contentEquals(outByte)'時,它試圖訪問一個已被鎖定的文件,並以「IOException異常:進程無法訪問文件,因爲另一個進程鎖定了一部分文件」 – amaidment