Java NIO2 File API介紹
[譯]Java NIO2 File API介紹
1.概覽
在這篇文章中,我們要關註的是使用Java平臺的NIO(譯者註: NIO意即New I/O)的APIs----NIO2----用來對文件做一些基礎的操作.
File APIs in NIO2 constitute one of the major new functional areas of the Java Platform that shipped with Java 7, specifically a subset of the new file system API alongside Path APIs .
2.設置
Setting up your project to use File APIs is just a matter of making this import:
使用File APIs只需要如下導包:
1 import java.nio.file.*;
由於本文的示例代碼可能會運行在不同的環境中(譯者註: 即不同的操作系統),因此我們不妨使用用戶的根目錄,這樣在所有操作系統中都是可行的:
1 private static String HOME = System.getProperty("user.home");
此Flies類是java.nio.file 包的主要切入點之一.此類提供了豐富的APIs用於讀,寫以及巧妙的處理文件和目錄(譯者註:目錄即文件夾).此Files類的方法作用於Path對象的實例.
3.檢查一個文件或目錄
於此文件系統中,我們可以用一個Path實例來表示一個文件或目錄.無論一個文件或者目錄存在與否,可獲取與否,都可以通過一個文件系統確認.
為了方便,不管何時使用文件這個術語,我們指的都是文件和目錄,除非特別指明(譯者註: 若非特別指出,本文中的文件兼指文件和目錄).
要確認一個文件存在,我們使用exists API:
1 @Test 2 public void givenExistentPath_whenConfirmsFileExists_thenCorrect() { 3 Path p = Paths.get(HOME); 4 5 assertTrue(Files.exists(p)); 6 }
要確認一個文件不存在,我們使用notExists API:
1 @Test 2 public voidgivenNonexistentPath_whenConfirmsFileNotExists_thenCorrect() { 3 Path p = Paths.get(HOME + "/inexistent_file.txt"); 4 5 assertTrue(Files.notExists(p)); 6 }
我們也可以確認一個文件是普通的文件還是目錄,使用isRegularFile API:
1 @Test 2 public void givenDirPath_whenConfirmsNotRegularFile_thenCorrect() { 3 Path p = Paths.get(HOME); 4 5 assertFalse(Files.isRegularFile(p)); 6 }
也有一些靜態的方法用於檢查文件的權限.檢查一個文件是可讀的,使用isReadable API:
1 @Test 2 public void givenExistentDirPath_whenConfirmsReadable_thenCorrect() { 3 Path p = Paths.get(HOME); 4 5 assertTrue(Files.isReadable(p)); 6 }
檢查一個文件是可寫的,使用isWritable API:
1 @Test 2 public void givenExistentDirPath_whenConfirmsWritable_thenCorrect() { 3 Path p = Paths.get(HOME); 4 5 assertTrue(Files.isWritable(p)); 6 }
同樣地,檢查其是可執行的:
1 @Test 2 public void givenExistentDirPath_whenConfirmsExecutable_thenCorrect() { 3 Path p = Paths.get(HOME); 4 assertTrue(Files.isExecutable(p)); 5 }
當我們有兩條路徑時,我們可以檢查是否他們都指向了文件系統下的同一個文件源:
1 @Test 2 public void givenSameFilePaths_whenConfirmsIsSame_thenCorrect() { 3 Path p1 = Paths.get(HOME); 4 Path p2 = Paths.get(HOME); 5 6 assertTrue(Files.isSameFile(p1, p2)); 7 }
4.創建文件
此文件系統的API提供了單線操作創建文件.創建文件(譯者註:非目錄)我們可以使用createFile API並傳遞一個Path對象來代表這個我們想要創建的文件(譯者註:非目錄).
所有在路徑中出現的名稱元素必須存在,除了文件(譯者註:非目錄)名.否則,將出現IOException異常:
1 @Test 2 public void givenFilePath_whenCreatesNewFile_thenCorrect() { 3 String fileName = "myfile_" + UUID.randomUUID().toString() + ".txt"; 4 Path p = Paths.get(HOME + "/" + fileName); 5 assertFalse(Files.exists(p)); 6 7 Files.createFile(p); 8 9 assertTrue(Files.exists(p)); 10 }
在上面的測試中,當我們第一次檢查路徑時,其並不存在,通過createFile()方法操作後,路徑存在.
創建目錄,可以使用createDirectory API:
1 @Test 2 public void givenDirPath_whenCreatesNewDir_thenCorrect() { 3 String dirName = "myDir_" + UUID.randomUUID().toString(); 4 Path p = Paths.get(HOME + "/" + dirName); 5 assertFalse(Files.exists(p)); 6 7 Files.createDirectory(p); 8 9 assertTrue(Files.exists(p)); 10 assertFalse(Files.isRegularFile(p)); 11 assertTrue(Files.isDirectory(p)); 12 }
此操作依然需要所有提及的路徑真實存在,若非如此,也將出現IOException:
1 @Test(expected = NoSuchFileException.class) 2 public void givenDirPath_whenFailsToCreateRecursively_thenCorrect() { 3 String dirName = "myDir_" + UUID.randomUUID().toString() + "/subdir"; 4 Path p = Paths.get(HOME + "/" + dirName); 5 assertFalse(Files.exists(p)); 6 7 Files.createDirectory(p); 8 }
然而,如果我們渴望一次調用就創建多級目錄,我們使用createDirectories方法.不似之前操作,當其遇任何路徑中缺省的名稱元素,並不拋IOException異常,而是遞歸地創建所有缺失的名稱元素:
1 @Test 2 public void givenDirPath_whenCreatesRecursively_thenCorrect() { 3 Path dir = Paths.get( 4 HOME + "/myDir_" + UUID.randomUUID().toString()); 5 Path subdir = dir.resolve("subdir"); 6 assertFalse(Files.exists(dir)); 7 assertFalse(Files.exists(subdir)); 8 9 Files.createDirectories(subdir); 10 11 assertTrue(Files.exists(dir)); 12 assertTrue(Files.exists(subdir)); 13 }
5.創建臨時文件
許多應用在運行時會在文件系統創建一系列臨時文件,結果是:大多數文件系統有專用的的目錄存儲由這些應用生成的臨時文件.
為此,新的文件系統API提供了專門的操作----createTempFile API.入參依次是一個Path對象,一個文件前綴,一個文件後綴:
1 @Test 2 public void givenFilePath_whenCreatesTempFile_thenCorrect() { 3 String prefix = "log_"; 4 String suffix = ".txt"; 5 Path p = Paths.get(HOME + "/"); 6 7 Files.createTempFile(p, prefix, suffix); 8 9 assertTrue(Files.exists(p)); 10 }
這些參數對於此操作時足夠的.然而,如果你需要定義文件的某個屬性,那麽可以使用第四個參數.
上面的測試創建了一個練市文件在HOME目錄,其前和其後分別加上了前綴和後綴字符串.最終文件名類似這樣:log_8821081429012075286.txt.
長整型字符串由系統生成.
然而,如果我們不提供前後綴,那麽此文件將只會包括那個長整型字符串和一個默認的.tmp擴展名:
1 @Test 2 public void givenPath_whenCreatesTempFileWithDefaults_thenCorrect() { 3 Path p = Paths.get(HOME + "/"); 4 5 Files.createTempFile(p, null, null); 6 7 assertTrue(Files.exists(p)); 8 }
上面的操作創建的文件名類似8600179353689423985.tmp.
最後,如果我們及不提供路徑,也不提供前後綴,那麽此操作將會使用默認輸出.磨人的文件生成位置為文件系統提供的臨時文件目錄.
1 @Test 2 public void givenNoFilePath_whenCreatesTempFileInTempDir_thenCorrect() { 3 Path p = Files.createTempFile(null, null); 4 5 assertTrue(Files.exists(p)); 6 }
在windows系統,類似這樣:
C:\Users\user\AppData\Local\Temp\6100927974988978748.tmp.
上面所有的操作,通過使用createTempDirectory替換掉createTempFile來創建臨時目錄.
6.刪除一個文件
刪除一個文件(非文件夾),我們使用delete API.為了言簡意賅,下面的測試首先確保了該文件(非文件夾)不存在,然後創建該文件並確保創建成功,最後刪除該文件(非文件夾)並確保其不再存在:
1 @Test 2 public void givenPath_whenDeletes_thenCorrect() { 3 Path p = Paths.get(HOME + "/fileToDelete.txt"); 4 assertFalse(Files.exists(p)); 5 Files.createFile(p); 6 assertTrue(Files.exists(p)); 7 8 Files.delete(p); 9 10 assertFalse(Files.exists(p)); 11 }
然而,如果某個文件(非文件夾)不存在於文件系統,刪除操作會失敗,並報錯IOException:
1 @Test(expected = NoSuchFileException.class) 2 public void givenInexistentFile_whenDeleteFails_thenCorrect() { 3 Path p = Paths.get(HOME + "/inexistentFile.txt"); 4 assertFalse(Files.exists(p)); 5 6 Files.delete(p); 7 }
我們可以避免這種情況通過使用deleteIfExists,即使文件(非文件夾)不存在,刪除也不會報錯.這是很重要的,當很多線程做刪除操作時,我們不希望看到僅僅因為一個線程在當前線程之前做刪除操作就報錯誤信息:
1 @Test 2 public void givenInexistentFile_whenDeleteIfExistsWorks_thenCorrect() { 3 Path p = Paths.get(HOME + "/inexistentFile.txt"); 4 assertFalse(Files.exists(p)); 5 6 Files.deleteIfExists(p); 7 }
當操作文件夾而非文件時,我們應該記住刪除操作默認不進行遞歸操作.因此,刪除非空文件夾會報錯IOException:
1 @Test(expected = DirectoryNotEmptyException.class) 2 public void givenPath_whenFailsToDeleteNonEmptyDir_thenCorrect() { 3 Path dir = Paths.get( 4 HOME + "/emptyDir" + UUID.randomUUID().toString()); 5 Files.createDirectory(dir); 6 assertTrue(Files.exists(dir)); 7 8 Path file = dir.resolve("file.txt"); 9 Files.createFile(file); 10 11 Files.delete(dir); 12 13 assertTrue(Files.exists(dir)); 14 }
7.復制文件
你可以復制文件或文件夾通過使用copy API:
1 @Test 2 public void givenFilePath_whenCopiesToNewLocation_thenCorrect() { 3 Path dir1 = Paths.get( 4 HOME + "/firstdir_" + UUID.randomUUID().toString()); 5 Path dir2 = Paths.get( 6 HOME + "/otherdir_" + UUID.randomUUID().toString()); 7 8 Files.createDirectory(dir1); 9 Files.createDirectory(dir2); 10 11 Path file1 = dir1.resolve("filetocopy.txt"); 12 Path file2 = dir2.resolve("filetocopy.txt"); 13 14 Files.createFile(file1); 15 16 assertTrue(Files.exists(file1)); 17 assertFalse(Files.exists(file2)); 18 19 Files.copy(file1, file2); 20 21 assertTrue(Files.exists(file2)); 22 }
如果目標文件已存在,則會復制失敗,除非指明REPLACE_EXISTING:
1 @Test(expected = FileAlreadyExistsException.class) 2 public void givenPath_whenCopyFailsDueToExistingFile_thenCorrect() { 3 Path dir1 = Paths.get( 4 HOME + "/firstdir_" + UUID.randomUUID().toString()); 5 Path dir2 = Paths.get( 6 HOME + "/otherdir_" + UUID.randomUUID().toString()); 7 8 Files.createDirectory(dir1); 9 Files.createDirectory(dir2); 10 11 Path file1 = dir1.resolve("filetocopy.txt"); 12 Path file2 = dir2.resolve("filetocopy.txt"); 13 14 Files.createFile(file1); 15 Files.createFile(file2); 16 17 assertTrue(Files.exists(file1)); 18 assertTrue(Files.exists(file2)); 19 20 Files.copy(file1, file2); 21 22 Files.copy(file1, file2, StandardCopyOption.REPLACE_EXISTING); 23 }
然而,當復制文件夾時,並不會進行遞歸操作.這意味著復制文件夾到一個新的位置生成的是空文件夾.
8.移動文件
你可以移動文件或目錄通過使用 move API.方式和copy操作很相似.如果copy操作和在圖形界面操作系統(譯者註:比如Windows)的復制粘貼類似,那麽move操作即和剪切相似:
1 @Test 2 public void givenFilePath_whenMovesToNewLocation_thenCorrect() { 3 Path dir1 = Paths.get( 4 HOME + "/firstdir_" + UUID.randomUUID().toString()); 5 Path dir2 = Paths.get( 6 HOME + "/otherdir_" + UUID.randomUUID().toString()); 7 8 Files.createDirectory(dir1); 9 Files.createDirectory(dir2); 10 11 Path file1 = dir1.resolve("filetocopy.txt"); 12 Path file2 = dir2.resolve("filetocopy.txt"); 13 Files.createFile(file1); 14 15 assertTrue(Files.exists(file1)); 16 assertFalse(Files.exists(file2)); 17 18 Files.move(file1, file2); 19 20 assertTrue(Files.exists(file2)); 21 assertFalse(Files.exists(file1)); 22 }
和copy操作一樣,如果目標文件已存在,move操作會失敗,除非指明了REPLACE_EXISTING:
1 @Test(expected = FileAlreadyExistsException.class) 2 public void givenFilePath_whenMoveFailsDueToExistingFile_thenCorrect() { 3 Path dir1 = Paths.get( 4 HOME + "/firstdir_" + UUID.randomUUID().toString()); 5 Path dir2 = Paths.get( 6 HOME + "/otherdir_" + UUID.randomUUID().toString()); 7 8 Files.createDirectory(dir1); 9 Files.createDirectory(dir2); 10 11 Path file1 = dir1.resolve("filetocopy.txt"); 12 Path file2 = dir2.resolve("filetocopy.txt"); 13 14 Files.createFile(file1); 15 Files.createFile(file2); 16 17 assertTrue(Files.exists(file1)); 18 assertTrue(Files.exists(file2)); 19 20 Files.move(file1, file2); 21 22 Files.move(file1, file2, StandardCopyOption.REPLACE_EXISTING); 23 24 assertTrue(Files.exists(file2)); 25 assertFalse(Files.exists(file1)); 26 }
9.總結
在本文中,我們學習了java7在新文件系統(NIO2)關於file 的APIs,了解了大多數重要的文件操作.
本文的示例代碼的源碼參看 https://github.com/eugenp/tutorials/tree/master/core-java-io
譯文原文地址:
http://www.baeldung.com/java-nio-2-file-api
Java NIO2 File API介紹