1. 程式人生 > >使用java.io.File的renameTo方法移動檔案失敗的問題

使用java.io.File的renameTo方法移動檔案失敗的問題

今天線上發現一個問題,發現一個定時移動檔案的業務沒有正常執行,結合日誌和程式碼發現,移動檔案是使用File類的renameTo方法,但是方法返回的都是false,表示檔案移動失敗。
出現這個問題我第一反應是不是檔案許可權的問題,但是和運維研究後發現的確不是許可權導致的。既然不是許可權的問題,那就看看renameTo的實現吧,檢視原始碼發現該方法最終是通過一個本地方法實現的,看不到咋寫的。
網上查了一下renameTo這個方法,發現這個方法確實存在一些問題,就是在不同的檔案系統中移動是不會成功的。因為測試環境並未出現這個問題,我就把生產環境和測試環境對比了下,發現測試環境下,檔案本身的目錄和要移動到的目錄是在/home下,而生產環境中,檔案本身目錄是在/home下,要移動到的目錄都是在/data下。於是用df命令查看了一下,發現 /home的檔案系統是/dev/sda3,型別是xfs的,/data的檔案系統是/dev/sdb1,型別是ext4。


既然是這樣那就寫個demo在自己的虛擬機器上驗證一下是不是這個原因導致的。

1.首先找兩個檔案系統不一樣的目錄,命令df -T
我們用/tmp 和 /run 作為測試目錄。
2.測試程式碼

import java.io.File;
/**
* 檔案移動方法測試
*/
public class FileTest {
  public static void main(String[] args) {
     String filePath="/tmp/test.txt";
     File file = new File(filePath);
     boolean b = file.renameTo(new File("/run/test.txt"));
     System.out.println(b);
 }
}

3.編譯執行

javac FileTest.java
java FileTest

執行結果輸出false,檔案也確實未移動成功

解決方法:使用apache的commons-io包中的工具類的進行檔案移動。
1.測試程式碼:

import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
/**
* 檔案移動方法測試
*/
public class FileTest {
  public static void main(String[] args) {
     String filePath="/tmp/test.txt";
     File file = new File(filePath);
     boolean b = file.renameTo(new File("/run/test.txt"));
     System.out.println(b);
     //使用apache的FileUtils工具
     try {
         FileUtils.moveFile(file,new File("/run/test.txt"));
         System.out.println("success");
     } catch (IOException e) {
         e.printStackTrace();
     }
 }
}

2.編譯執行

javac -cp /root/jar/commons-io-2.4.jar FileTest.java
java -cp /root/jar/commons-io-2.4.jar: FileTest

執行結果成功移動檔案
3.apache的FileUtils移動檔案方法的主要實現如下:

//先使用renameTo方法進行移動
boolean rename = srcFile.renameTo(destFile);
if (!rename) {
	//renameTo移動失敗,就複製檔案,然後刪除原檔案
	copyFile( srcFile, destFile );
	if (!srcFile.delete()) {
	FileUtils.deleteQuietly(destFile);
	throw new IOException("Failed to delete original file '" + srcFile +
		"' after copy to '" + destFile + "'");
	}
}

總結:

  1. 檔案移動最好不要使用Java的renameTo方法,而是應該使用apache的commons-io包,當然也可以自己封裝類似的方法。
  2. renameTo方法移動失敗是檔案系統不同造成的,補充測試發現不同的檔案系統,就算型別相同,移動也會失敗。