IO는 파일의 속성 정보를 읽기 위해 File 클래스만 제공하지만, NIO는 좀 더 다양한 파일의 속성 정보를 제공해주는 클래스와 인터페이스를 java.nio.file
, java.nio.file.attribute
패키지에서 제공하고 있다.
Path
는 IO의java.io.File
클래스에 대응되는 NIO 인터페이스이다.
java.nio.file.Paths
클래스의 정적 메소드인 get()
메소드를 호출하면 된다.Path path = Paths.get(String first, String ... more);
Path path = Paths.get(URI uri);
get()
메소드의 매개값은 파일의 경로인데, 문자열로 지정할 수도 있고, URI 객체로 지정할 수도 있다. "C:\Temp\dir\file.txt\"
Path path = Paths.get("C:/Temp/dir/file.txt");
Path path = Paths.get("C:/Temp/dir", "file.txt");
Path path = Paths.get("C:", "Temp", "dir", "file.txt");
"C:\Temp\dir1"
이라면 "C:\Temp\dir2\file.txt"
는 다음과 같이 지정이 가능하다.Path path = Paths.get("../dir2/file.txt");
운영체제의 파일 시스템은
FileSystem
인터페이스를 통해서 접근할 수 있다.
FileSystem
구현 객체는 FileSystems
의 정적 메소드인 getDefault()
로 얻을 수 있다.FileSystem fileSystem = FileSystems.getDefault();
리턴 타입 | 메소드(매개 변수) | 설명 |
---|---|---|
Iterable<FileStore> | getFileStores() | 드라이버 정보를 가진 FileStore 객체들을 리턴 |
Iterable<Path> | getRootDirectories() | 루트 디렉토리 정보를 가진 Path 객체들을 리턴 |
String | getSeparator() | 디렉토리 구분자 리턴 |
FileStore
는 드라이버를 표현한 객체이다.리턴 타입 | 메소드(매개 변수) | 설명 |
---|---|---|
long | getTotalSpace() | 드라이버 전체 공간 크기(단위: 바이트) 리턴 |
long | getUnallocatedSpace() | 할당되지 않은 공간 크기(단위: 바이트) 리턴 |
long | getUsableSpace() | 사용 가능한 공간 크기, getUnallocatedSpace()와 동일한 값 |
boolean | isReadOnly() | 읽기 전용 여부 |
String | name() | 드라이버명 리턴 |
String | type() | 파일 시스템 종류 |
java.nio.file.Files
클래스는 파일과 디렉토리의 생성 및 삭제, 그리고 이들의 속성을 읽는 메소드를 제공하고 있다.
속성
이란 파일이나 디렉토리가 숨김인지, 디렉토리인지, 크기가 어떻게 되는지, 소유자가 누구인지에 대한 정보를 말한다.java.nio.file.Files
클래스가 제공하는 정적 메소드리턴 타입 | 메소드(매개 변수) | 설명 |
---|---|---|
long 또는 Path | copy(...) | 복사 |
Path | createDirectories(...) | 모든 부모 디렉토리 생성 |
Path | createDirectory(...) | 경로의 마지막 디렉토리만 생성 |
Path | createFile(...) | 파일 생성 |
void | delete(...) | 삭제 |
boolean | deleteIfExists(...) | 존재한다면 삭제 |
boolean | exists(...) | 존재 여부 |
FileStore | getFileStore(...) | 파일이 위치한 FileStore(드라이브) 리턴 |
FileTime | getLastModifiedTime(...) | 마지막 수정 시간 리턴 |
UserPrincipal | getOwner(...) | 소유자 정보 리턴 |
boolean | isDirectory(...) | 디렉토리인지 여부 |
booelan | isExecutable(...) | 실행 가능 여부 |
boolean | isHidden(...) | 숨김 여부 |
boolean | isReadable(...) | 읽기 가능 여부 |
booelan | isRegularFile(...) | 일반 파일인지 여부 |
boolean | isSameFile(...) | 같은 파일인지 여부 |
boolean | isWritable(...) | 쓰기 가능 여부 |
Path | move(...) | 파일 이동 |
BufferedReader | newBufferedReader(...) | 텍스트 파일을 읽는 BufferedReader 리턴 |
BufferedWriter | newBufferedWriter(...) | 텍스트 파일에 쓰는 BufferedWriter 리턴 |
SeekableByteChannel | newByteChannel(...) | 파일에 읽고 쓰는 바이트 채널을 리턴 |
DirectoryStream<Path> | newDirectoryStream(...) | 디렉토리의 모든 내용을 스트림으로 리턴 |
InputStream | newInputStream(...) | 파일의 InputStream 리턴 |
OutputStream | newOutputStream(...) | 파일의 OutputStream 리턴 |
boolean | notExists(...) | 존재하지 않는지 여부 |
String | probeContentType(...) | 파일의 MIME 타입을 리턴 |
byte[] | readAllBytes(...) | 파일의 모든 바이트를 읽고 배열로 리턴 |
List<String> | readAllLines(...) | 텍스트 파일의 모든 라인을 읽고 리턴 |
long | size(...) | 파일의 크기 리턴 |
Path | write(...) | 파일에 바이트나 문자열을 리턴 |
와치 서비스(WatchService)는 자바 7에서 처음 소개된 것으로 디렉토리 내부에서 파일 생성, 삭제, 수정 등의 내용 변화를 감시하는데 사용된다.
FileSystem
의 newWatchService()
메소드를 호출하면 된다.WatchService watchService = FileSystems.getDefault().newWatchService();
WatchService
를 생성했다면 감시가 필요한 디렉토리의 Path
객체에서 register()
메소드로 WatchService
를 등록하면 된다. StandardWatchEventKinds
상수로 지정할 수 있다. path.register(watchService, StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
WatchService
를 등록한 순간부터 디렉토리 내부에서 변경이 발생되면 와치 이벤트(WatchEvent)
가 발생하고, WatchService
는 해당 이벤트 정보를 가진 와치키(WatchKey)
를 생성하여 큐(Queue)
에 넣어준다. WatchService
의 take()
메소드를 호출하여 WatchKey
가 큐에 들어올 때까지 대기하고 있다가 WatchKey
가 큐에 들어오면 WatchKey
를 얻어 처리하면 된다.while(true) {
WatchKey watchKey = watchService.take(); //큐에 WatchKey가 들어올 때까지 대기
}
WatchKey
를 얻고나서 해야할 일은 pollEvents()
메소드를 호출해서 WatchEvent
리스트를 얻어내는 것이다. 한 개의 WatchEvent
가 아니라 List<WatchEvent<?>>
로 리턴하는 이유는 여러 개의 파일이 동시에 삭제, 수정, 생성될 수 있기 때문이다. WatchEvent
는 파일당 하나씩 발생한다.List<WatchEvent<?>> list = watchKey.pollEvents();
WatchEvent
리스트에서 WatchEvent
를 하나씩 꺼내어 이벤트의 종류와 Path
객체를 얻어낸 다음 적절히 처리하면 된다.for(WatchEvent watchEvent : list) {
//이벤트 종류 얻기
Kind kind = watchEvent.kind();
//감지된 Path 얻기
Path path = (Path)watchEvent.context();
//이벤트 종류별로 처리
if(kind == StandardWatchEventKinds.ENTRY_CREATE) {
//생성되었을 경우, 실행할 코드
} else if(kind == StandardWatchEventKinds.ENTRY_DELETE) {
//삭제되었을 경우, 실행할 코드
} else if(kind == StandardWatchEventKinds.ENTRY_MODIFY) {
//변경되었을 경우, 실행할 코드
} else if(kind == StandardWatchEventKinds.OVERFLOW) {
}
}
OVERFLOW
이벤트는 운영체제에서 이벤트가 소실되었거나 버려진 경우에 발생하므로 별도의 처리 코드가 필요 없다. 따라서 CREATE
, DELETE
, MODIFY
이벤트만 처리하면 된다. WatchEvent
가 발생하면 큐에 다시 들어가기 때문에, 한 번 사용된 WatchKey
는 reset()
메소드로 초기화해야 한다.reset()
메소드는 true
를 리턴하지만, 감시하는 디렉토리가 삭제되었거나 키가 더 이상 유효하지 않을 경우에는 false
를 리턴한다. WatchKey
가 더 이상 유효하지 않게 되면 무한 루프를 빠져나와 WatchService
의 close()
메소드를 호출하고 종료하면 된다.while(true) {
WatchKey watchKey = watchService.take();
List<WatchEvent<?>> list = watchKey.pollEvents();
for(WatchEvent watchEvent : list) {
...
}
boolean valid = watchKey.reset();
if(valid) { break; }
}
watchService.close();
이것이 자바다 책