JAVA NIO:Path ,File

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();
    }
});
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章