JAVA NIO path
參考地址:http://tutorials.jenkov.com/java-nio/path.html
JAVA NIO Path爲JAVA 7中JAVA NIO新增的接口。完整包名爲: java.nio.file.Path
JAVA PATH指向的是文件系統裏的一個路徑,可以指向文件,也可以指向目錄,可以用絕對路徑表示,也可以用相對路徑表示。
注:java.nio.file.Path接口與文件系統中環境變量path無關。
Java NIO path實例創建**
通過java.nio.file.Paths.get()方法即可創建Path實例。
可通過絕對路徑創建:
//通過絕對路徑創建,windows os
Path path=Paths.get("D://test/test.txt");
//通過絕對路徑創建,linux
Path path1=Paths.get("/home/opt/app/test/test.txt");
如果windows環境下以絕對路徑創建時,以/開頭,如:
/home/jakobjenkov/myfile.txt
創建path實例時會解析在前面加上磁盤所在目錄,即,解析成:
C:/home/jakobjenkov/myfile.txt
也可以通過相對路徑創建,通過Paths.get(basePath, relativePath)方法,
//全路徑爲d:\\data\\projects
Path projects = Paths.get("d:\\data", "projects");
//全路徑爲d:\\data\projects\\a-project\\myfile.txt
Path file = Paths.get("d:\\data", "projects\\a-project\\myfile.txt");
. 與 ..
. 表明當前目錄
.. 表明上級目錄
如
Path currentDir = Paths.get("d:\\data\\projects\.\a-project");
指明的路徑爲:
d:\data\projects\a-project
String path = "d:\\data\\projects\\a-project\\..\\another-project";
指明的路徑爲:
d:\\data\\projects\\another-project
Path.normalize()
格式化path,即移除路徑中的 . 與 ..,
如:
String originalPath ="d:\\data\\projects\\a-project\\..\\another-project";
Path path1 = Paths.get(originalPath);
System.out.println("path1 = " + path1);
Path path2 = path1.normalize();
System.out.println("path2 = " + path2);
輸出爲:
path1 = d:\data\projects\a-project\..\another-project
path2 = d:\data\projects\another-project
path2會移除..
JAVA NIO Files
完整包名java.nio.file.Files,與java.nio.file.Path 一起使用。
介紹幾個基本的方法,其他方法參考javaDoc
Files.exists()
根據給定的path判斷文件是否存在。
boolean exists(Path path, LinkOption… options)
示例如下:
Path path=java.nio.file.Paths.get("data/logging.properties");
boolean pathExists=java.nio.file.Files.exists(path, new LinkOption[]{ LinkOption.NOFOLLOW_LINKS});
通過Files.exist()方法判斷路徑是否存在時,必須先指定一個Path實例。默認情況下符號鏈接會被追蹤。
第二個參數是一個LinkeOption數組,決定文件是否存在。LinkOption.NOFOLLOW_LINKS表明不追蹤符號鏈接。如果目錄或文件存在,則返回true,如果目錄或文件不存在或者不知道其存在性,則返回false。
在對文件進行操作前,需要調用exists()來判斷文件是否存在。
Files.noExist()
根據給定的path判斷文件是否不存在。boolean notExists(Path path, LinkOption… options)
默認情況,符號鏈接是會跟從的。但是,如果傳遞LinkOption.NOFOLLOW_LINKS參數,符號鏈接就不會跟從了。如果目錄或文件不存在,則返回true,如果目錄或文件存在或者不知道其存在性,則返回false。
注: !exists(path) 不等於 notExists(path)(因爲 !exists() 不一定是原子的,而 notExists() 是原子的)。同時,如果 exists() 和 notExists() 都返回false,則說明文件的存在性不清楚。最後,類似於訪問性判斷方法,這些方法的結果也是會瞬間過時的,因此,在安全性敏感的應用中應該避免至少應改謹慎使用。
Files.createDirectory()
創建目錄,目錄已存在情況下拋FileAlreadyExistsException,其他情況拋IOException,如下:
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();
}
Files.copy()
文件的複製,目標文件已存在情況下拋
java.nio.file.FileAlreadyExistsException,其他情況下會拋IOException
示例如下
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();
}
StandardCopyOption.REPLACE_EXISTING: 表明如果目標文件已存在,覆蓋。
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();
}
StandardCopyOption.REPLACE_EXISTING:表明如果目標文件已存在,覆蓋。
Files.delete()
文件刪除。注:只能刪除空文件 。
Path path = Paths.get("data/subdir/logging-moved.properties");
try {
Files.delete(path);
} catch (IOException e) {
//deleting file failed
e.printStackTrace();
}
Files.walkFileTree()
遞歸讀取path下的所有文件,完整方法Files.walkFileTree(Path, FileVisitor );可通過實現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 {
}
在調用Files.walkFileTree(Path, FileVisitor )之前,必須自己實現 FileVisitor 接口,然後將該實現的一個實例當作參數傳入到walkFileTree()方法中。對於walkFileTree()讀取的每一個文件,都會調用FileVisitor中實現的方法。如果不想自己實現FileVisitor 接口,可以直接繼承類SimpleFileVisitor ,SimpleFileVisitor 實現了FileVisitor 接口中的每一個方法。具體見例子
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;
}
});
其中:
preVisitDirectory():在每次訪問目錄前,會調用該方法。
postVisitDirectory():在每次訪問目錄後,會調用該方法
visitFile():在訪問文件時,會調用該方法,注意此處是文件,不包括目錄 。
visitFileFailed():訪問文件失敗時,會調用該方法。如沒訪問權限會導致訪問文件失敗。
這四個方法都返回一個FileVisitResult,該實現是一個枚舉類型,以此決定文件遞歸訪問是否繼續,包括四個值:
- CONTINUE:表明文件訪問繼續
- TERMINATE:表明文件訪問中止
- SKIP_SIBLINGS:表明訪問文件訪問繼續,但不再訪問當前訪問文件或者目錄的兄弟結點
- SKIP_SUBTREE:表明訪問文件繼續,但不再訪問該目錄下的其他文件 。僅當 preVisitDirectory()返回該值時有意義,如果其他方法返回該值,意味着與CONTINUE意義相同。
文件查詢
以下是一個通過繼承類SimpleFileVisitor 來調用walkFileTree() 來實現查詢文件README.txt的例子
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()遞歸刪除目錄的例子。在visitFile()和postVisitDirectory()裏進行文件刪除的動作file.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();
}
Java NIO AsynchronousFileChannel
異步從文件中讀取、寫入數據。
AsynchronousFileChannel創建
通過AsynchronousFileChannel.open()創建一個AsynchronousFileChannel.
Path path = Paths.get("data/test.xml");
AsynchronousFileChannel fileChannel =
AsynchronousFileChannel.open(path, StandardOpenOption.READ);
第二個參數StandardOpenOption.READ表明文件以讀的方式打開。
異步讀
通過調用AsynchronousFileChannel.read()方法讀取數據,有兩種方法
- 通過Future,即read()方法返回Future,如下所示:
Future<Integer> operation = fileChannel.read(buffer, 0);
- 通過CompletionHandler,即將CompletionHandler實例傳入read()方法,如下所示:
fileChannel.read(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("result = " + result);
attachment.flip();
byte[] data = new byte[attachment.limit()];
attachment.get(data);
System.out.println(new String(data));
attachment.clear();
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
});
通過Future讀
Future<Integer> operation = fileChannel.read(buffer, 0);
buffer:即從channel中讀取數據到buffer中。
0: 表明從文件中讀取的位置。
該read()方法立即返回,此時讀操作可能並未完全完成,可通過返回的Future實例的isDone()來判斷讀操作是否完成 。示例如下:
AsynchronousFileChannel fileChannel =
AsynchronousFileChannel.open(path, StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
long position = 0;
Future<Integer> operation = fileChannel.read(buffer, position);
while(!operation.isDone());
buffer.flip();
byte[] data = new byte[buffer.limit()];
buffer.get(data);
System.out.println(new String(data));
buffer.clear();
以上例子中,通過循環調用operaion.isDone()來判斷讀是否完成 。
注:該例子僅供參考,並未考慮CPU效率 。
通過CompletionHandler讀
fileChannel.read(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("result = " + result);
attachment.flip();
byte[] data = new byte[attachment.limit()];
attachment.get(data);
System.out.println(new String(data));
attachment.clear();
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
}
});
通過向read()方法中傳入一個CompletionHandler實例來實現異步讀,當讀操作完成時,會調用 completed()方法,讀操作失敗時,會調用failed()方法。
異步寫
從buffer中寫入到channel中,與異步讀類似,異步寫也可以通過兩種方法
- 通過Future,即write()方法返回Future,如下所示:
Future<Integer> operation = fileChannel.write(buffer, position);
- 通過CompletionHandler,即將CompletionHandler實例傳入write()方法
通過返回Future異步寫
示例如下:
Path path = Paths.get("data/test-write.txt");
AsynchronousFileChannel fileChannel =
AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
long position = 0;
buffer.put("test data".getBytes());
buffer.flip();
Future<Integer> operation = fileChannel.write(buffer, position);
buffer.clear();
while(!operation.isDone());
System.out.println("Write done");
首先以寫的方式open一個channel. StandardOpenOption.WRITE
其次,將數據放入buffer中。
再次,調用channel.write(buffer,position)方法,從buffer中寫入channel,返回Future
最後,循環調用future.isDone()判斷寫操作是否完成,完成後做完成後續操作。
通過CompletionHandler異步寫
即向write()中傳入一個ComletionHandlder實例,寫操作完成時,會調用completed()方法,寫操作失敗時,會調用failed()方法。
Path path = Paths.get("data/test-write.txt");
if(!Files.exists(path)){
Files.createFile(path);
}
AsynchronousFileChannel fileChannel =
AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
ByteBuffer buffer = ByteBuffer.allocate(1024);
long position = 0;
buffer.put("test data".getBytes());
buffer.flip();
fileChannel.write(buffer, position, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
System.out.println("bytes written: " + result);
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("Write failed");
exc.printStackTrace();
}
});