您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關Java中怎么操作NIO2文件,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
從classpath中讀取過文件的人,都知道需要寫一些讀取流的方法,很是繁瑣。最近使用IDEA在打出.這個符號的時候,一行代碼讓人激動不已:竟然提供直接讀出bytes字節的方法。
byte[] bytes = Test .class .getResourceAsStream("/test.txt") .readAllBytes();
這真是太讓人振奮了,再也不用寫一些丑陋的,還容易忘記關閉流的代碼了。
可惜的是,代碼提示給了當頭一棒。這需要Java的9版本以上才能支持。這就像一個還有1年壽命的患者看到救命的藥,還需要兩年才能問世的感覺,是一樣的。
廢話少說,我們用不了這些函數,難道Java7+的也用不了么(有些可憐的寶貝們還真用不了)?話說回來,JAVA7+對NIO進行了增強,主要在對文件操作部分做了大量的改進。它體現在,將File操作進行分離、封裝、改進,最終形成Path(Paths)、Files、FileSystem(FileSystems)三個主要類。
我們親切的叫它NIO2。
其中,Paths、Files中提供了大量便捷的靜態操作方法;NIO2還提供了有關文件權限(屬性)操作、軟連接、文件查找等高級API,使得NIO2具有更全面的文件系統操作接口。
新入行的小鮮肉可能一開始就接觸這個了,但對我們一些老程序員來說,突然看到這些東西,就像打開了一個新大陸。所以本文面向的是還不知道這些個操作的菜鳥,以及懶癌晚期的老Java程序員。
1、Path
文件系統都是Tree或者層級結構來組織文件的,任何一個節點可以是一個目錄或者一個文件,在NIO2中稱為Path,這和原來的File有很多相似之處,只是Path具有更多的表述語義。
1.1、基本屬性
以下代碼展示了它的基本屬性。
Path path = Paths.get("/data/logs/web.log"); //屬性 //獲取路徑中的文件名或者最后一個節點元素 System.out.printf("FileName:%s%n", path.getFileName()); //路徑節點元素的格式 System.out.printf("NameCount:%s%n", path.getNameCount()); //遍歷路徑節點方法1 Iterator<Path> names = path.iterator(); int i = 0; while (names.hasNext()) { Path name = names.next(); System.out.printf("Name %s:%s%n",i,name.toString()); i++; } //方法2 for(int j = 0; j < path.getNameCount(); j++) { System.out.printf("Name %s:%s%n",j,path.getName(j)); } //父路徑 System.out.printf("Parent:%s%n",path.getParent()); //跟路徑,比如"/"、"C:";如果是相對路徑,則返回null。System.out.printf("Root:%s%n",path.getRoot()); //子路徑,結果中不包含root,前開后閉 System.out.printf("Subpath[0,2]:%s%n",path.subpath(0,2));
結果:
FileName:web.log NameCount:3 Name 0:data Name 1:logs Name 2:web.log Name 0:data Name 1:logs Name 2:web.log Parent:/data/logs Root:/ Subpath[0,2]:data/logs
1.2、路徑轉換
1)比如“/data/logs/./web.log”、“/data/logs/../db”這種包含“冗余”路徑的,有時候我們需要轉換為正常路徑語句,則使用“Path.normalize()”方法,無比的清爽清潔。
Path path = Paths.get("/data/logs/../web.log"); //輸出結果:/data/web.log System.out.printf("%s%n",path.normalize());
2)如果文件需要被外部資源訪問(resource),你可以通過Path.toUri()來轉換,path對應的文件或者目錄可以不存在,此方法不會check異常:
Path path = Paths.get("/data/logs/web.log"); //表示本地文件的uri,輸出結果:file:///data/logs/web.log System.out.printf("%s%n",path.toUri());
3)toAbsolutePath():如果路徑為相對路徑,則轉換為絕對路徑,對于JAVA程序而言,起始路徑為classpath。此方法不會檢測文件是否真的存在或者有權限。
4)其中toRealPath()是比較重要的方法,不過它會對文件是否存在、訪問權限進行檢測,需要捕獲異常。首先檢測文件是否存在、是否有權限;如果path為相對路徑,則將會轉換為絕對路徑,同“3)”;如果是“符號連接”文件(軟連接),則獲取其實際target路徑(除非指定了NO_FOLLOW_LINKS);如果路徑中包含“冗余”,則移除,同1)。這個方法,通常用于對“用戶輸入的path”進行校驗和轉換,使用比較多。
5)resolve():路徑合并,當前path與參數進行路徑合并,append。
6)relativize():獲取相對路徑,“/data”與“/data/logs/p1”的相對路徑為“logs/p1”,反之為“../../”。
2、Files
Files類中提供了大量靜態方法,用于實現文件(目錄)的創建、復制、遷移、刪除以及訪問文件數據等操作。
2.1、檢測文件或目錄
Files.exists(Path)和notExists(Path)兩個方法,這兩個方法都會實際檢測文件或者目錄是否存在、以及是否有訪問權限。注意:!exist() 與notExists()并不完全相等,exist可能有三種狀態:如果不存在或者安全校驗不通過則返回false,如果返回true則表示文件確實存在且有權限。notExists()檢測類似,對于沒有通過安全校驗的也會返回false;當exists與notExists同時返回false時,說明文件不可以驗證(即無權限),所以通常這兩個方法需要同時使用。
判斷文件(目錄)具有讀、寫、執行的權限,可以通過如下方法:
Path path = Paths.get("data/logs/web.log"); boolean isRegularExecutableFile = Files.isRegularFile(path) & Files.isReadable(path) & Files.isExecutable(path);
有時候,兩個不同的path,會指向同一個文件,比如當一個path是軟連接時,此時可以使用Files.isSameFile(p1,p2)來檢測,當然你可以通過Path + LinkOption相關組合獲取target實際path來比較。
2.2、刪除
delete和deleteIfExists兩個方法均可刪除文件,前者嘗試刪除的文件如果不存在則會拋出異常。如果文件是軟連接,則只刪除連接文件而不會刪除target文件,如果path為目錄,則目錄需要為空,否則刪除失敗(IOException)。在刪除操作之前,最后做一些常規的檢測,比如文件是否存在(有權限)、目錄是否為空等。稍后我們再介紹“遞歸刪除目錄樹和文件”。
2.3、文件(目錄)復制
copy(Path,Path,CopyOption…)方法可以復制文件,不過,需要注意CopyOption的相關選項。
當copy一個軟連接文件時,默認將會復制target文件,如果只想復制軟連接文件而不是target內容,可以指定NOFOLLOW_LINKS選項。CopyOption的實現類為StandardCopyOption,此外CopyOption也擴展了LinkOption,即包含NOFOLLOW_LINKS選項。如下為CopyOption選項列表:
1)REPLACE_EXISTING:如果目標文件已經存在,則直接覆蓋;如果目標文件是個軟連接,則軟連接文件本身被覆蓋(而非連接文件的target文件);如果復制的是目錄,且目標目錄不為空時,則會拋出異常(DirectoryNotEmptyException),稍后介紹“遞歸復制目錄樹和文件”。此參數通常必選。復制目錄時,目標目錄會自動創建,源目錄中如果有文件,則不會復制文件,只會創建空的目標目錄。source和target,要么同時是目錄、要么同時是文件。
2)COPY_ATTRIBUTES:復制文件時,也同時復制目標文件的屬性(metadata),對于文件屬性(FileAttribute)的支持依賴于文件系統(和平臺),不過lastModifiedTime通常會被復制。
3)NOFOLLOW_LINKS:繼承自LinkOption,表示如果文件是軟連接,則不followed,即只復制連接文件,不復制其target實際文件內容。
4)ATOMIC_MOVE:只支持move操作,copy不支持。
Path source = Paths.get("/data/logs/web.log"); Path target = Paths.get("/data/logs/web.log.copy"); Files.copy(source,target,REPLACE_EXISTING,COPY_ATTRIBUTES,NOFOLLOW_LINKS);
2.4、移動
move(Path,Path,CopyIOption),基本原則同copy。需要注意,如果是目錄,目錄中包含文件時也可以移動的(這可能依賴于平臺),子目錄也一起移動,但是目標目錄必須為空(DirectoryNotEmptyException)。基本語義同“mv -rf”,目標目錄不需要提前創建,move結束后,源目錄將不存在。支持兩種選項:
1)REPLACE_EXISTING:如果目標文件已存在,則覆蓋;如果目標文件是軟連接,則連接文件被覆蓋但是其指向不會受影響。
2)ATOMIC_MOVE:原子復制,需要平臺的文件系統支持(不支持則拋出異常),指定此參數時其他選項將被忽略;如果文件不能被原子復制(或者替換),則會拋出AtomicMoveNotSupportedException。
2.5、打開文件
Files類中提供了多個靜態的方法,用于直接讀寫文件。如下為文件打開的幾個選項參數(StandardOpenOptions):
1)WRITE: 打開文件用于write訪問。
2)APPEND:在文件尾部追加數據,伴隨用于WRITE或CREATE選項。
3)TRUNCATE_EXISTING:將文件truncate為空,伴隨用于WRITE選項。比如,文件存在時,將文件數據清空并重新寫入。
4)CREATE_NEW:創建新文件,如果文件已存在則拋出異常。
5)CREATE:如果文件已存在則直接打開,否則創建文件。
6)DELETE_ON_CLOSE:當文件操作關閉時則刪除文件(close方法或者JVM關閉時),此選項適用于臨時文件(臨時文件不應該被其他進程并發訪問)。
7)SPARSE:創建一個“稀疏”文件,伴隨使用CREATE_NEW,適用于某些特殊的文件系統比如NTFS,這些大文件允許出現“gaps”(空洞)在某些情況下可以提高性能且這些gaps不消耗磁盤空間。
8)SYNC:對文件內容(data)或者metadata的修改,都會同步到底層存儲。
9)DSYNC:對文件內容的修改,會同步到底層存儲。
對于一些小文件(M級別),通常我們希望一次全部讀取所有內容,而不再使用傳統的方式迭代讀取。
//全部讀取小文件中的數據 //Files.readAllBytes(Paths.get("/data/web.log")); List<String> lines = Files.readAllLines(Paths.get("/data/web.log"),Charset.forName("utf-8")); //將準備好的數據,直接全部寫入文件。(打開、寫入) Files.write(Paths.get("/data/web-1.log"),lines,Charset.forName("utf-8"), StandardOpenOption.APPEND, StandardOpenOption.CREATE)); //傳統操作 try (BufferedReader reader = Files.newBufferedReader(Paths.get("/data/web.log"))) { while (true) { String line = reader.readLine(); if (line == null) { break; } System.out.println(line); } } catch (IOException e) { // } //傳統操作 try (BufferedWriter writer = Files.newBufferedWriter(Paths.get("/data/web.log"),Charset.forName("utf-8"),StandardOpenOption.APPEND, StandardOpenOption.CREATE)) { for(String line : lines) { writer.write(line); } } catch (IOException e) { // }
此外Files中還提供了基于buffer的channel操作,返回類型為SeekableByteChannel,這種操作通常適用于讀或者寫,以及數據可以基于buffer進行拆封包,其他特性類似“隨機訪問文件”。(Files.newByteChannel(),功能同File.open())
Path path = Paths.get("/data/web.log"); //創建一個普通文件,如果已存在則拋出異常,文件屬性為默認。Files.createFile(path); //創建臨時文件,臨時文件的目錄和前綴可以為null,后綴如果為null時默認使用".tmp"; Files.createTempFile(null,null,".tmp"); //創建一個軟連接文件 Files.createSymbolicLink(Paths.get("/data/link.log"),Paths.get("/data/web.log"));
2.6、Metadata管理
2.6.1、BasicFileAttributes
BasicFileAttributes基本接口,提供了一些基本的文件metadata,比如lastAccessTime、lastModifiedTime等,它的實現類因平臺而已有:DosFileAttributes、PosixFileAttribute、UnixFileAttribute等;不同平臺所能支持的屬性有所不同。(在跨平臺場景下,你可能需要使用FileStore來判斷當前文件系統是否支持相應的FileAttributeView)
Path path = Paths.get("/data/logs/web.log"); BasicFileAttributes attributes = Files.readAttributes(path,BasicFileAttributes.class); System.out.println("regular file:" + attributes.isRegularFile()); System.out.println("directory:" + attributes.isDirectory()); System.out.println("symbolic link:" + attributes.isSymbolicLink()); System.out.println("modified time:" + attributes.lastModifiedTime().toMillis()); //修改系統更新屬性 Files.setLastModifiedTime(path,FileTime.fromMillis(System.currentTimeMillis())); //修改其他屬性 Files.setAttribute(path,"dos:hidden",true);
屬性名格式為“view-name:attribute-name”,比如“dos:hidden”;其中合法的view-name目前有“basic”、“posix”、“unix”、“owner”(所有者信息,權限),屬性的列表需要根據自己的平臺對應相應的Attributes類,否則會導致設置異常。
2.6.2、PosixFileAttributes
Path path = Paths.get("/data/logs/web.log"); PosixFileAttributes attributes = Files.readAttributes(path,PosixFileAttributes.class); //用戶組和權限 UserPrincipal userPrincipal = attributes.owner(); System.out.println(userPrincipal.toString()); GroupPrincipal groupPrincipal = attributes.group(); System.out.println(groupPrincipal.toString()); Set<PosixFilePermission> permissions = attributes.permissions(); //將權限轉換為文件屬性,用于創建新的文件,目前文件權限也是一種屬性 FileAttribute<Set<PosixFilePermission>> fileAttribute = PosixFilePermissions.asFileAttribute(permissions); Files.createFile(Paths.get("/data/test.log"),fileAttribute); //修改文件權限,可以在permissions中增減權限列表,枚舉 Files.setPosixFilePermissions(path,permissions);
從權限字符串中,構建權限列表
Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("-rw-r--r--"); Files.setPosixFilePermissions(path, permissions);
修改文件(目錄)的所有者或者所在組:
Path path = Paths.get("/data/logs/web.log"); //首先找到系統中的其他用戶,根據用戶名 UserPrincipal userPrincipal = path.getFileSystem().getUserPrincipalLookupService().lookupPrincipalByName("userName"); Files.setOwner(path,userPrincipal); //或者 Files.getFileAttributeView(path,FileOwnerAttributeView.class).setOwner(userPrincipal); //修改group GroupPrincipal groupPrincipal = path.getFileSystem().getUserPrincipalLookupService().lookupPrincipalByGroupName("groupName"); Files.getFileAttributeView(path,PosixFileAttributeView.class).setGroup(groupPrincipal);
2.6.3、UserDefinedFileAttributeView
用戶自定義文件屬性(即擴展文件屬性),以及屬性值的長度,這取決于底層文件系統或者平臺是否支持。目前,主流的平臺和文件系統都支持擴展文件屬性,比如FreeBSD(ZFS)、Linux(ext3、ext4、ZFS)、MacOS等。默認情況下,此操作是開啟的,如果已關閉,可以通過“sudo mount -o remount,user_xattr {你的文件系統掛載路徑}”,否則也會拋出UnsupportedOperationException。
Path path = Paths.get("/data/logs/web.log"); UserDefinedFileAttributeView view = Files.getFileAttributeView(path,UserDefinedFileAttributeView.class); String name = "user.mimetype"; int size = view.size(name);//我個人認為JDK應該直接支持獲取屬性值,而不是再周折一番 ByteBuffer buffer = ByteBuffer.allocate(size); view.read(name,buffer); buffer.flip(); String value = Charset.defaultCharset().decode(buffer).toString(); System.out.println(value); //其他操作,比如list獲取所有屬性的列表等。//寫入或者跟新自定義屬性 view.write(name,Charset.defaultCharset().encode("text/html"));
3、FileSystem
FileStore是新增的API,用于描述底層存儲系統,一個平臺有多個FileStore,我們可以通過FileSystem獲取FileStore的列表,以及每個store的存儲狀態、文件列表等。
Path path = Paths.get("/data/logs/web.log"); path.getFileSystem().getFileStores();//獲取文件所屬的文件系統的所有的存儲器。//當前文件系統所能支持的FileAttributeView,此后可以對文件使用相應的view獲取或者修改屬性 Set<String> viewNames = FileSystems.getDefault().supportedFileAttributeViews(); System.out.println(viewNames);//basic,unix,posix,owner,dos等 //或者,全局 //遍歷所有的磁盤存儲 Iterable<FileStore> fileStores = FileSystems.getDefault().getFileStores();//獲取默認文件系統的所有存儲器 for(FileStore store : fileStores) { System.out.println("\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-\-"); System.out.println("className:" + store.getClass().getName()); System.out.println("name:" + store.name());//磁盤名稱 System.out.println("type:" + store.type());//類型 System.out.println("readOnly:" + store.isReadOnly());//是否為只讀 System.out.println("usableSpace:" + store.getUsableSpace() + "/" + store.getTotalSpace()); boolean supported = store.supportsFileAttributeView(BasicFileAttributeView.class); //或者 //boolean supported = store.supportsFileAttributeView("basic"); //fileStore的屬性,不同于FileAttributes,這些屬性應該與FileStore的各個實現類對應。Long totalSpace = (Long)store.getAttribute("totalSpace"); System.out.println(totalSpace);
可以獲取每個FileStore支持的FileAttributeView,此后即可通過Files.getFileAttributeView()來獲取相應的視圖類。每個view都有簡寫名稱,比如BasicFileAttributeView.name()返回其簡寫名稱為“basic”。目前JDK支持很多FileAttributeView,比如BasicFileAttributeView、UnixFileAttributeView等等,以及允許自定義的UserDefinedFileAttributeView等。JDK將獲取文件屬性、修改文件屬性的操作全部基于對應的FileAttributeView類來實現。
PosixFileAttributeView view = Files.getFileAttributeView(path,PosixFileAttributeView.class); PosixFileAttributes fileAttributes = view.readAttributes();
此外,根據FileStore的底層存儲不同,有多種實現,可以參看FileStore的實現類,比如UnixFileStore、BsfFileStore(Mac)等,每個FileStore所能支持的attribute也不太相同,需要根據其對應的實現類獲取,也可以使用FileStore.getAttribute()來獲取,但是屬性名需要與類中支持的屬性名對應。
4、目錄操作
JAVA中目錄也用Path表示,其基本屬性與File一樣。
4.1、列舉根目錄
//遍歷文件系統的所有根目錄 Iterable<Path> roots = FileSystems.getDefault().getRootDirectories(); for(Path root : roots) { System.out.print(root); }
4.2、創建、刪除
Path dir = Paths.get("/data/xyz"); Files.createDirectories(dir); Files.createDirectory(dir);
其中createDirectory()方法是一個“嚴格校驗”的方法,如果父路徑不存在則會拋出異常,如果路徑已經存在或者同名文件存在則會拋出異常,簡單來說此方法只能創建最后一級目錄(且此前不存在)。對于createDirectories()方法,比較兼容,會逐層校驗并創建父路徑,如果不存在則創建。
創建目錄時,也可以像文件那樣,指定文件屬性(包括權限)
Path dir = Paths.get("/data/xyz/12x/123x"); Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("\-rw\-rw\-\-\-\-"); FileAttribute<Set<PosixFilePermission>> fileAttribute = PosixFilePermissions.asFileAttribute(permissions); Files.createDirectories(dir,fileAttribute);
4.3、遍歷目錄
Path dir = Paths.get("/data"); DirectoryStream<Path> stream = Files.newDirectoryStream(dir); for (Path path : stream) { System.out.println(path); } stream.close();
stream方式會遍歷指定目錄下的所有文件,包括文件。目錄。連接、隱藏文件或者目錄等。
此外,JDK還支持基于filter模式,在遍歷時過濾“非必要”的文件或者目錄。
Path dir = Paths.get("/data"); DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() { @Override public boolean accept(Path entry) throws IOException { return Files.isRegularFile(entry); } }; DirectoryStream<Path> stream = Files.newDirectoryStream(dir,filter); for (Path path : stream) { System.out.println(path); } stream.close();
4.4、Glob文件過濾器
NIO2中新增支持了基于Glob的文件過濾器,一種類似于正則表達式的匹配語法;glob是來自unlix(shell指令)用于文件匹配的表達式,很多主流語言和平臺(dos、window)都支持,不過不同語言對glob的支持程度不同(需要注意)。
1、*:匹配任意多個任意字符,包括空字符(none)。比如“/data/*.log”匹配“data”目錄下所有以“.log”結尾的文件。
2、**:類似于*,匹配任意多個字符,不過它可以越過目錄分割符,此語法通常用于匹配全路徑。比如:“/data/**”
3、?:只匹配一個字符。
4、\:轉義符,用于轉義特殊字符,比如“\”、“-”、“{”、“}”、“[”等等。比如需要匹配“{”那么其字面表達式為“\{”,“\\”用于匹配單斜杠。
5、!:非,不包含,通常配合[]使用。(貌似不支持^)
6、{}:指定一組子規則,比如:
1){sum,moon,stars}:匹配“sun”或者“moon”或者“starts”(其一即可),子規則使用“,”分割。
2){temp*,tmp*}:匹配以temp或者tmp開頭的所有字符串。
7、[]:匹配一組字符串中的單個字符,如果字符串集中包含“-”則匹配區間中單個字符。比如[abc]匹配“a”或者“b”或者“c”,[a-z]匹配a到z小寫字符中的一個,[0-9]匹配0~9任意一個數字。可以混合使用,比如[abce-g]匹配“a”、“b”、“c”、“e”、“f”、“g”,[!a-c]匹配除a、b、c之外的任意一個字符。復合子規則使用“,”分割,比如[a-z,0-9]匹配a~z或者0~9任意一個字符。
在[]中,“*”、“?”、“\”只匹配其自己(字面),如果“-”在[]內且是第一個字符或者在!之后,也匹配自己。
8、文件中的前導字符、“.”將作為普通字符對待。比如“*”可以匹配“.tmp”這個文件,FIles.isHidden()可以用來檢測此文件為隱藏文件。
9、其他所有字符匹配其自己(取決于因平臺而已的實現方式,包括路徑分隔符等)。
示例:
1、*.html匹配任意“.html”結尾的文件。
2、???:匹配任意三個字符(包括數字字符)
3、*[0-9]*:匹配包含一個數字的任意多個字符。
4、*.{htm,html},匹配任意以“html”或者“htm”結尾的字符串。
GLobbing表達式,一種比較便捷的過濾策略,對于一些簡單的操作(主要是只根據文件或者路徑名特性匹配時),可以不使用Filter的情況下完成,當然glob的內部實現仍然是基于封裝的Filter來實現(PathMatcher);但是glob語法相對簡單,JDK NIO2有關文件過濾表達式,可以同時支持glob和正則表達式。稍后介紹如何使用PathMatcher來遍歷文件或者目錄樹。
Path dir = Paths.get("/data"); //內部,默認會對glob表達式增加前綴,glob,為了兼容PathMatcher DirectoryStream<Path> stream = Files.newDirectoryStream(dir,"\*.txt"); for (Path path : stream) { System.out.println(path); } stream.close();
4.5、操作鏈接文件
硬連接(或者連接):
1)文件有相同的 inode 及 data block;
2)只能對已存在的文件進行創建;
3)不能交叉文件系統進行硬鏈接的創建;
4)不能對目錄進行創建,只可對文件創建;
5)刪除一個硬鏈接文件并不影響其他有相同 inode 號的文件。
軟連接(符號連接):軟鏈接與硬鏈接不同,若文件用戶數據塊中存放的內容是另一文件的路徑名的指向,則該文件就是軟連接。軟鏈接就是一個普通文件,只是數據塊內容有點特殊。軟鏈接有著自己的 inode 號以及用戶數據塊。因此軟鏈接的創建與使用沒有類似硬鏈接的諸多限制:
1)軟鏈接有自己的文件屬性及權限等;
2)可對不存在的文件或目錄創建軟鏈接;
3)軟鏈接可交叉文件系統;
4)軟鏈接可對文件或目錄創建;
5)創建軟鏈接時,鏈接計數 i_nlink 不會增加;
6)刪除軟鏈接并不影響被指向的文件,但若被指向的原文件被刪除,則相關軟連接被稱為死鏈接(即 dangling link,若被指向路徑文件被重新創建,死鏈接可恢復為正常的軟鏈接)。
##創建連接 ln 目標 連接名 ## 創建軟連接 ln \-s 目標 連接名 ##查看軟連接目標指向,對于硬連接是不顯示。ls \-l 軟連接文件 ##通過stat指令可以查看軟硬連接的inode和block信息 ##發現硬連接與目標文件的信息完全一致。##軟連接文件有單獨的inode和block。
創建連接
Path target = Paths.get("/data/test.log"); //target必須存在 Files.createLink(Paths.get("/data/hard\-link.log"),target); Files.createSymbolicLink(Paths.get("/data/soft\-link.log"),target); //檢測文件是否為軟連接問題 boolean isSymbolicLink = Files.isSymbolicLink(Paths.get("/data/soft\-link.log")); Path target2 = Files.readSymbolicLink(Paths.get("/data/soft\-link.log")); //檢測是否為同一個文件 System.out.println(Files.isSameFile(target,target2));//true System.out.println(Files.isSameFile(target,Paths.get("/data/hard\-link.log")));//true System.out.println(Files.isSameFile(target,Paths.get("/data/soft\-link.log")));//true 通過Files.isSameFile()比較,我們會發現,無論是軟連接、硬鏈接,都與目標文件是同一個文件。
4.6、查找文件
前文中介紹了有關PathMatcher,在JAVA NIO2中用于匹配文件的表達式,可以支持glob和正則表達式(regex)兩種方式。其中glob的語法更接近linux shell,regex是更廣泛、更豐富的一種方式。比如文件:/data/test.log
Path path = Paths.get("/data/test.log"); PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:\*\*.log"); System.out.println(pathMatcher.matches(path)); //基于正則 pathMatcher = FileSystems.getDefault().getPathMatcher("regex:.\*\\\\.log"); System.out.println(pathMatcher.matches(path));
表達式規則:syntax:pattern,其中合法的syntax為“glob”和“regex”;需要注意這兩種表達式的區別。內部實現也比較簡單,對于glob字符串將會轉化為正則表達式字符串,然后統一使用正則匹配。
4.7、遞歸遍歷目錄樹
曾經,使用JAVA遍歷文件數是一件比較繁瑣的事情,在NIO2中增加了原生提供了此操作。主要API為FileVisitor,其簡單實現類為SimpleFileVisitor:
1、preVisitDirectory:在瀏覽目錄之前。前置操作。比如遍歷并復制文件時,可以在進入目錄之前,創建遷移的目標新目錄。
2、postVisitDirectory:在瀏覽目錄中所有文件之后(瀏覽其他目錄之前)。后置操作。
3、visitFile:瀏覽文件,Path和BaseFileAttributes會傳遞入方法。
4、visitFileFailed:瀏覽文件失敗時調用,比如文件屬性無法獲取、目錄無法打開等異常時,調用此方法,同時傳入Path和Exception。
簡單的遍歷(查找、篩選匹配)
Path dir = Paths.get("/data/redis"); Stream<Path> stream = Files.walk(dir); stream.forEach(path \-> { System.out.println(path); }); stream.close();
復雜遍歷(遍歷查找、文件遷移校驗)
public static void main(String\[\] args) throws Exception{ Path dir = Paths.get("/data/redis"); Files.walkFileTree(dir,new Finder()); } public static class Finder implements FileVisitor<Path> { @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { System.out.println("preVisitDirectory:" + dir); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { System.out.println("visitFile:" + file); return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException { System.out.println("visitFileFailed:" + file + ",exception:" + (exc != null ? exc.getMessage() : "\-")); return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { System.out.println("postVisitDirectory:" + dir); return FileVisitResult.CONTINUE; } }
FileVisitResult用于表示執行狀態:
1)CONTINUE:表示繼續執行(繼續下一步操作)
2)TERMINATE:終止遞歸遍歷,其他的后續方法不會被執行,尚未瀏覽的文件也將不會被訪問。
3)SKIP_SUBTREE:跳過子樹,即當前目錄以及其子目錄都將被跳過。適用在preVisitDirectory(),其他方法返回此值則等效于CONTINUE。
4)SKIP_SIBLINGS:跳過此文件(或者目錄)的同級文件或者文件,適用在postVisitDirectory(),如果preVisitDirectory返回此值,則當前目錄也會跳過,且postVisitDirectory()不會被執行。
看完上述內容,你們對Java中怎么操作NIO2文件有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。