Files

Programming/Java NIO 2015. 9. 15. 17:37

Java NIO Files 클래스(java.nio.file.Files)는 파일 시스템에서 파일들을 조작하기 위한 여러가지 메소드들을 제공한다. 이 Java NIO Files 튜토리얼은 이러한 메소드들의 가장 일반적인 사용을 다룰 것이다. Files 클래스는 많은 메소드들을 포함하므로, 여기에 설명되지 않은 메소드들이 필요할 경우 JavaDoc를 확인한다. Files 클래스는 그것을 위한 메소드를 가지고 있을지도 모른다.


java.nio.file.Files 클래스는 java.nio.file.Path 객체와 함께 동작하므로, Files 클래스의 작업을 할 수 있기 이전에 Path 클래스의 이해가 필요하다.

Files.exists()


Files.exists() 메소드는 파일 시스템의 주어진 Path가 존재하는지 확인한다.


이는 파일 시스템의 존재하지 않는 Path 객체의 생성을 가능하게 한다. 예를 들어, 새로운 디렉토리를 만들 계획이라면, 먼저 그에 부합하는 Path 객체를 만들고 난뒤, 디렉토리를 만들 것이다.


Path 객체들은 파일 시스템의 존재하는 경로들을 가리키고 있거나 아닐수도 있기 때문에 그것들을 작업할 수 있는지(이러한 경우 확인 필요) 결정하기 위해 Files.exists() 메소드를 사용할 수 있다.


다음 Java Files.exists()의 예제가 있다:

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

boolean pathExists =
        Files.exists(path,
            new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});

이 예제에서 먼저 존재 여부를 확인하고자 하는 경로를 가리키는 Path 객체를 생성한다. 그 뒤에 Path 객체가 첫번째 매개변수로 사용되는 Files.exists() 메소드를 호출한다.


Files.exists() 메소드의 두번째 매개변수에 주목하기 바란다. 이 매개변수는 경로에 존재 여부를 결정하는 Files.exists()의 방법에 영향을 미치는 설정들의 배열이다. 위 예제에서 배열은 LinkOption.NOFOLLOW_LINKS를 포함하는데, 이는 Files.exists() 메소드가 경로의 존재 여부를 판단하는데 있어 파일 시스템의 심볼릭 링크를 따르지 않음을 의미한다.

Files.createDirectory()


Files.createDirectory() 메소드는 Path 객체로부터 새로운 디렉토리를 생성하는 메소드이다. 다음 Java Files.createDirectory()의 예제가 있다:

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 객체는 새롭게 경로를 만들어 가리켜 반환된다.


만약 디렉토리가 이미 존재한다면, java.nio.file.FileAlreadyExistsException는 던져질 것이다. 만약 다른 이상이 발생한다면, IOException으로 던져질수 있다. 예를 들어, 원하는 새로운 디렉토리의 부모 디렉토리가 존재하지 않는다면 IOException으로 던져질수 있다. 부모 디렉토리는 만들고자 하는 새로운 디렉토리이다. 그러므로 이는 새로운 디렉토리의 부모 디렉토리를 의미한다.

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()를 호출한다. 이는 소스 경로에 의해 참조된 파일이 대상 경로에 의해 참조된 파일에 복사 될 것이다.


만약 대상 파일이 이미 존재한다면, java.nio.file.FileAlreadyExistsException가 던져진다. 다른 이상이 발생한다면, IOException이 던져질 것이다. 예를 들어, 파일을 복사할 디렉토리가 존재하지 않는다면 IOException이 던져질 것이다.

Overwriting Existing Files


Files.copy()로 존재하는 파일에 강제로 덮어쓰기를 하는 것은 가능하다. 다음 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() 메소드에 세번째 매개변수에 주목하길 바란다. 이 매개변수는 copy() 메소드가 만약 대상 파일이 이미 존재할 경우 존재하는 파일에 덮어쓰기 할 것을 지시한다.

Files.move()


Java NIO Files 클래스는 하나의 경로로부터 또다른 경로에 파일들을 옮기기 위한 기능들도 포함한다. 파일 옮기기는 같은 작업에서 그것을 다른 디렉토리로 옮기고 이름을 변경할 수 있다는 것을 제외하면 이름을 변경하는 것과 같다. 그렇다, java.io.File 클래스는 renameTo() 메소드로 그것을 작업할 수 있지만, 이제 java.nio.file.Files 클래스로도 파일 이동 작업을 할 수 있다.


다음 Java 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() 메소드는 만약 파일 이동이 실패하면 IOException 던질수도 있다. 예를 들어, StandardCopyOption.REPLACE_EXISTING를 설정 하지 않고  파일이 이미 대상 경로에 존재하거나, 이동하기 위한 파일이 존재하지 않을 경우 등이 있다.

Files.delete()


Files.delete() 메소드는 파일이나 디렉토리를 삭제할 수 있는 메소드이다. 다음 Java 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() 메소드가 호출된다. 만약 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 구현의 각 메소드들은 디렉토리를 순회하는 동안 서로 다른 시간에 호출될 것이다. 만약 이 메소드들 모두를 거칠 필요가 없다면, FileVisitor 인터페이스의 모든 메소드들의 기본 구현을 포함한 SimpleFileVisitor 클래스를 확장할 수 있다.


다음 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의 enum 객체를 반환한다. FileVisitResult  enum은 다음 네가지 설정을 포함한다:

  • CONTINUE
  • TERMINATE
  • SKIP_SIBLINGS
  • SKIP_SUBTREE

호출된 메소드의 다음 값들 중 하나의 반환으로 어떻게 파일 방문을 계속 해야할 지 결정할 수 있다.


CONTINUE는 파일 방문을 정상적으로 계속 할 것을 의미한다.


TERMINATE는 파일 방문을 지금 종료해야 할 것을 의미한다.


SKIP_SIBLINGS는 파일 방문을 계속 해야하지만, 이 파일이나 디렉토리의 어떠한 형제에도 방문하지 않음을 의미한다.


SKIP_SUBTREE는 파일 방문을 계속 해야하지만, 이 디렉토리의 전체에는 방문하지 않음을 의미한다. 이 값은 preVisitDirectory()로부터 반환된 경우에만 기능을 갖고 있다. 만약 나머지 다른 메소드들로부터 반환되었다면 이는 CONTINUE로 해석될 것이다.

Searching For Files


다음 README.txt라는 이름의 파일을 찾기 위해 SimpleFileVisitor를 확장하여 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();
}

Deleting Directories Recursively


Files.walkFileTree()는 하나와 디렉토리 안에 모든 파일과 서브 디렉토리를 삭제하기 위해 사용되기도 한다. Files.delete() 메소드는 만약 디렉토리가 비어있을 경우 오직 하나의 디렉토리만을 삭제할 것이다. 모든 디렉토리를 통한 방문과 각 디렉토리에서 모든 파일들을(visitFile()의 내부) 삭제함으로써, 추후 디렉토리 자체를(postVisitDirectory()의 내부) 삭제하여 모든 서브 디렉토리와 파일들과 함께 디렉토리를 삭제할 수 있다. 다음 디렉토리를 재귀하여 삭제하는 예제이다:

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();
}

Additional Methods in the Files Class


java.nio.file.Files 클래스는 심볼릭 링크는 만들거나, 파일 크기를 결정하거나, 파일 권한을 설정하는 등의 많은 유용한 기능들을 포함한다. 이 메소드들에 관한 추가적인 정보는 java.nio.file.Files 클래스의 JavaDoc을 확인하길 바란다.



<원문 출처>


'Programming > Java NIO' 카테고리의 다른 글

AsynchronousFileChannel  (0) 2015.09.15
Path  (0) 2015.09.15
vs. IO  (0) 2015.09.15
Pipe  (0) 2015.09.15
DatagramChannel  (0) 2015.09.11
Posted by 레미파
,