16 Java NIO Files-翻譯

王金龍發表於2017-12-06

Java NIO Files類提供了一系列方法來操作檔案系統中的檔案。這個Java NIO Files教程將會包含大部分常用的方法。Files類包含許多的方法,所以如果你需要的方法沒有在這裡描述,也需要檢視JavaDoc。

java.nio.file.Files類跟java.nio.Paths例項一起工作。所以在與Files一起工作時你需要了解Path類。

Files.exists()方法

Files.exists()方法會檢查一個給定的Path是否在檔案系統中存在。

也可以建立在檔案系統中不存在的Path例項。例如,如果你想要建立一個新的目錄,你先會建立相應的Path例項,然後建立目錄。

由於Path例項可能在檔案系統中並不存在,你可以使用Files.exists()方法來檢查。

下面是Files.exists()方法的例子:

Path path = Paths.get("data/logging.properties");

boolean pathExists =
        Files.exists(path,
            new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});
複製程式碼

這個例子首先建立了一個我們需要檢查檔案是否存在的Path例項。然後,這個例子呼叫了Files.exists()方法並將Path例項作為第一個引數。

注意到Files.exists()方法的第二個引數,這個引數是選項陣列來供方法Files.exist()來判斷檔案是否存。在以上的例子中,這個陣列包含了LinkOption.NOFOLLOW_LINKS,這說明這Files.exists()方法不應該檢查符號連線來判斷檔案是否存在。

Files.createDirectory()

Files.createSirectory方法通過Path例項來建立一個新的目錄。下面是Java的Files.createDirecotry()的例子。

Path path = Paths.get("data/subdir");

try {
    Path newDir = Files.createDirectory(path);
} catch(FileAlreadyExistsException e){
    // the directory already exists.
} catch (IOException e) {
    //something else went wrong
    e.printStackTrace();
}
複製程式碼

第一行建立了一個Path例項來代表需要建立的目錄。在try-catch塊的內部呼叫了Files.createDirectory()方法,並將path作為第一個引數。如果目錄建立成功,會返回一個指向新建立目錄的Path例項。

如果目錄已經存在,會丟擲java.nio.FileAlreadyExistsException異常。如果出現其他情況,會出現IO異常。

Files.copy()

Files.copy()方法用於將檔案複製到另一個檔案。下面是Java NIO的Files.copy()的例子:

Path sourcePath      = Paths.get("data/logging.properties");
Path destinationPath = Paths.get("data/logging-copy.properties");

try {
    Files.copy(sourcePath, destinationPath);
} catch(FileAlreadyExistsException e) {
    //destination file already exists
} catch (IOException e) {
    //something else went wrong
    e.printStackTrace();
}
複製程式碼

首先這個例子建立了一個源Path和目標Path。然後,這個例子呼叫了Files.copy()方法,並將兩個path例項作為引數。這會將源Path中指定的檔案複製到目標path中。

如果目標檔案已經存在,將會丟擲java.nio.file.FileAlreadyExistsException異常。如果出現其它異常,會丟擲IOException。例如,如果目錄檔案的目錄不存在,就會丟擲IOException。

覆蓋存在檔案

也可以實現在檔案複製時覆蓋已經存在的檔案。下面是一個如何使用Files.copy()方法的一個例子。

Path sourcePath      = Paths.get("data/logging.properties");
Path destinationPath = Paths.get("data/logging-copy.properties");

try {
    Files.copy(sourcePath, destinationPath,
            StandardCopyOption.REPLACE_EXISTING);
} catch(FileAlreadyExistsException e) {
    //destination file already exists
} catch (IOException e) {
    //something else went wrong
    e.printStackTrace();
}
複製程式碼

注意到Files.copy()方法的第三個引數。這個參數列明複製時覆蓋已經存在的檔案。

File.move()方法

Java NIO Files類包含了從一個路徑移動到另一個路徑的方法。移動檔案與重新命名檔案是一致的。移動檔案除了能夠移動檔案之外還能重新命名檔案。是的,在java.io.File中可以通過renameTo()方法來實現,但是現在在java.nio.file.Files也可以實現這個功能。

下面是Files.move()的例子:

Path sourcePath      = Paths.get("data/logging-copy.properties");
Path destinationPath = Paths.get("data/subdir/logging-moved.properties");

try {
    Files.move(sourcePath, destinationPath,
            StandardCopyOption.REPLACE_EXISTING);
} catch (IOException e) {
    //moving file failed.
    e.printStackTrace();
}
複製程式碼

首先源路徑和目標路徑會被建立。源路徑指定的是需要移動的檔案,目標路徑指定的是原始檔需要被移動到的位置。然後呼叫了Files.move()方法。結果是檔案被移動了。

注意到Files.move()方法的第三個引數,這個引數告訴Files.move()方法如果目標檔案存在,則覆蓋。這個引數事實上是可選的。

Files.move()方法可能會丟擲一個IO異常如果移動檔案時出現異常。例如,如果一個檔案已經在目標路徑上存在,但沒有指定StandardCopyOPtion.REPLACE_EXISTING或者原始檔不存在等其它情況。

Files.delete()

Files.delete()方法可以刪除一個檔案或目錄。下面是一個Files.delete()的例子:

Path path = Paths.get("data/subdir/logging-moved.properties");

try {
    Files.delete(path);
} catch (IOException e) {
    //deleting file failed
    e.printStackTrace();
}
複製程式碼

首先建立了需要刪除檔案的Path例項。然後,呼叫Files.delete()方法。如果刪除檔案失敗或其它原因,會丟擲IOEXception。

Files.walkFileTree()

Files.walkFileTree()用來遞迴遍歷目錄。walkFileTree方法需要一個Path引數和一個FileVisitor引數。Path引數指向需要遍歷的目錄,FileVisitor是在遍歷時需要呼叫的。

在我解析遍歷器工作原理之前,我們先來看一下FileVisitor介面:

public interface FileVisitor {

    public FileVisitResult preVisitDirectory(
        Path dir, BasicFileAttributes attrs) throws IOException;

    public FileVisitResult visitFile(
        Path file, BasicFileAttributes attrs) throws IOException;

    public FileVisitResult visitFileFailed(
        Path file, IOException exc) throws IOException;

    public FileVisitResult postVisitDirectory(
        Path dir, IOException exc) throws IOException {

}
複製程式碼

FileVisitor介面必須自己實現,並傳遞一個具體的例項給walkFileTree方法。在訪問目錄期間,每個FileVisitor的實現會在不同的時候被呼叫。需要你不想要實現FileVsitor的全部方法,可以繼承SimpleFileVisitor類,它包含了FileVisitor的預設實現。

下面是walkFileTree()的例子:

Files.walkFileTree(path, new FileVisitor<Path>() {
  @Override
  public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
    System.out.println("pre visit dir:" + dir);
    return FileVisitResult.CONTINUE;
  }

  @Override
  public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
    System.out.println("visit file: " + file);
    return FileVisitResult.CONTINUE;
  }

  @Override
  public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
    System.out.println("visit file failed: " + file);
    return FileVisitResult.CONTINUE;
  }

  @Override
  public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
    System.out.println("post visit directory: " + dir);
    return FileVisitResult.CONTINUE;
  }
});
複製程式碼

FileVisitor的不同方法會在遍歷的不同的時候被呼叫:

preVisitDirectory方法會在訪問任何目錄前被呼叫。postVisitDirectory方法會在訪問目錄之後被呼叫。

visitFile方法會在每次訪問檔案時被呼叫。它不是在訪問目錄時被呼叫,而是在訪問檔案時被呼叫。visitFileFailed方法會在訪問檔案失敗時被呼叫。例如,沒有許可權訪問檔案或其他情況。

這四個方法的每個方法都返回了一個FileVisitResult列舉例項。FileVisitResult列舉包含以下幾個選項:

  • CONTINUE
  • TERMINATE
  • SKIP_SIBLINGS
  • SKIP_SUBTREE

通過返回這些值呼叫者可以確定後續需要怎麼做。

CONTINUE 意味著檔案遍歷按正常繼續。

TERMINATE 表示檔案遍歷應該結束。

SKIP_SIBLINGS 表示遍歷應該繼續,但不繼續訪問相鄰的檔案或目錄。

SKIP_SUBTREE 表示檔案遍歷應該繼續,但不訪問子目錄。這個值只會在preVisitDirectory中返回,如果在其它函式中返回,會被解析成CONTINUE。

查詢檔案

下面是一個繼承SimpleFileVisitor來查詢README.txt檔案的walkFileTree()方法的使用例子:

Path rootPath = Paths.get("data");
String fileToFind = File.separator + "README.txt";

try {
  Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
    
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
      String fileString = file.toAbsolutePath().toString();
      //System.out.println("pathString = " + fileString);

      if(fileString.endsWith(fileToFind)){
        System.out.println("file found at path: " + file.toAbsolutePath());
        return FileVisitResult.TERMINATE;
      }
      return FileVisitResult.CONTINUE;
    }
  });
} catch(IOException e){
    e.printStackTrace();
}

複製程式碼

遞迴刪除目錄

Files.walkFileTree()方法也可以用來刪除指定目錄下的檔案和子目錄。Files.delete()方法僅會刪除一個空目錄。通過遍歷所有的目錄並刪除每個目錄下的檔案,然後刪除目錄自己。下面是一個遞迴刪除目錄的例子:

Path rootPath = Paths.get("data/to-delete");

try {
  Files.walkFileTree(rootPath, new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
      System.out.println("delete file: " + file.toString());
      Files.delete(file);
      return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
      Files.delete(dir);
      System.out.println("delete dir: " + dir.toString());
      return FileVisitResult.CONTINUE;
    }
  });
} catch(IOException e){
  e.printStackTrace();
}
複製程式碼

Files類中的其他方法

java.nio.file.Files類下包含其他的許多方法,如建立符號連結,檢測檔案的大小,設定檔案許可權等等。請從java.nio.file.Files的JavaDoc檢視這些方法的明細。

相關文章