NIO.2的文件IO和文件系統
Java7之後提供了全面的文件IO和文件系統訪問支持,並且還支持異步的Channel
Path、Paths和Files核心API
NIO.2引入了一個Path接口,Path接口代表一個平臺無關的平臺路徑,此外NIO.2還提供了Files和Paths兩個工具類,其中Files包含了大量靜態的工具類方法來操作文件,Paths則包含了兩個返回Path的靜態工廠方法
import java.io.*;
import java.net.*;
import java.nio.file.*;
public class PathTest
{
public static void main(String[] args)
throws Exception
{
// 以當前路徑來創建Path對象
Path path = Paths.get(".");
System.out.println("path裏包含的路徑數量:"
+ path.getNameCount());
System.out.println("path的根路徑:" + path.getRoot());
// 獲取path對應的絕對路徑。
Path absolutePath = path.toAbsolutePath();
System.out.println(absolutePath);
// 獲取絕對路徑的根路徑
System.out.println("absolutePath的根路徑:"
+ absolutePath.getRoot());
// 獲取絕對路徑所包含的路徑數量
System.out.println("absolutePath裏包含的路徑數量:"
+ absolutePath.getNameCount());
System.out.println(absolutePath.getName(3));
// 以多個String來構建Path對象
Path path2 = Paths.get("g:", "publish", "codes");
System.out.println(path2);
}
}
import java.nio.file.*;
import java.nio.charset.*;
import java.io.*;
import java.util.*;
public class FilesTest
{
public static void main(String[] args)
throws Exception
{
// 複製文件
Files.copy(Paths.get("FilesTest.java"),
new FileOutputStream("a.txt"));
// 判斷FilesTest.java文件是否爲隱藏文件
System.out.println("FilesTest.java是否爲隱藏文件:"
+ Files.isHidden(Paths.get("FilesTest.java")));
// 一次性讀取FilesTest.java文件的所有行
List<String> lines = Files.readAllLines(Paths
.get("FilesTest.java"), Charset.forName("gbk"));
System.out.println(lines);
// 判斷指定文件的大小
System.out.println("FilesTest.java的大小爲:"
+ Files.size(Paths.get("FilesTest.java")));
List<String> poem = new ArrayList<>();
poem.add("水晶潭底銀魚躍");
poem.add("清徐風中碧竿橫");
// 直接將多個字符串內容寫入指定文件中
Files.write(Paths.get("pome.txt"), poem,
Charset.forName("gbk"));
// 使用Java 8新增的Stream API列出當前目錄下所有文件和子目錄
Files.list(Paths.get(".")).forEach(path -> System.out.println(path));
// 使用Java 8新增的Stream API讀取文件內容
Files.lines(Paths.get("FilesTest.java"), Charset.forName("gbk"))
.forEach(line -> System.out.println(line));
FileStore cStore = Files.getFileStore(Paths.get("C:"));
// 判斷C盤的總空間,可用空間
System.out.println("C:共有空間:" + cStore.getTotalSpace());
System.out.println("C:可用空間:" + cStore.getUsableSpace());
}
}
FileVisitor遍歷文件和目錄
Files類提供瞭如下兩個方法來遍歷文件和子目錄:
- walkFileTree(Path start, FileVisitor<? super Path> visitor): 遍歷start路徑下的所有文件和子目錄
- walkFileTree(Path start, Set< FileVisitOption > options, int maxDepth, FileVisitor<? super Path>visitor): 該方法跟上一個類似,但最多遍歷maxDepth深度的文件
兩個方法都需要FileVisitor參數,FileVisitor代表一個文件訪問器,walkFileTree()方法會自動遍歷start路徑下的所有文件和子目錄,遍歷文件和子目錄都會觸發FileVisitor中相應的方法
FileVisitor中定義瞭如下4個方法:
- FileVisitResult postVisitDirectory(T dir, IOException exc):訪問子目錄之後觸發該方法
- FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs):訪問子目錄之前觸發該方法
- FileVisitResult visitFile(T file, BasicFileAttributes attrs):訪問file文件時觸發該方法
- FileVisitResult visitFileFailed(T file, IOException exc):訪問file文件失敗時候觸發該方法
這4個方法都返回一個FileVisitResult對象,它是一個枚舉類,代表訪問了之後的後續行爲(FileVisitResult):
- CONTINUE: 代表“繼續訪問”的後續行爲
- SKIP_SIBLINGS:代表“繼續訪問”的後續行爲。。。。。。
- SKIP_SUBTREE:代表“繼續訪問”的後續行爲。。。。。。
- TERMINATE:代表“中止訪問”的後續行爲。。。。。。
實際編程時,可以通過繼承SimpleFileVisitor(FileVisitor的實現類)來實現自己的文件訪問器
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
public class FileVisitorTest
{
public static void main(String[] args)
throws Exception
{
// 遍歷g:\publish\codes\15目錄下的所有文件和子目錄
Files.walkFileTree(Paths.get("g:", "publish", "codes", "15"),
new SimpleFileVisitor<Path>()
{
// 訪問文件時候觸發該方法
@Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attrs) throws IOException
{
System.out.println("正在訪問" + file + "文件");
// 找到了FileInputStreamTest.java文件
if (file.endsWith("FileInputStreamTest.java"))
{
System.out.println("--已經找到目標文件--");
return FileVisitResult.TERMINATE;
}
return FileVisitResult.CONTINUE;
}
// 開始訪問目錄時觸發該方法
@Override
public FileVisitResult preVisitDirectory(Path dir,
BasicFileAttributes attrs) throws IOException
{
System.out.println("正在訪問:" + dir + " 路徑");
return FileVisitResult.CONTINUE;
}
});
}
}
WatchService監控文件變化
之前的實現方式是,啓動一個後臺線程監控文件,每隔一段時間去遍歷一下指定目錄的文件,如果發現結果不一樣,則判定文件發生了變化,NIO.2的Path類提供瞭如下方法來監聽文件系統的變化
- register(WatchService watcher, WatchEvent.Kind<?>… events):用watcher監聽該path代表的目錄下的文件變化,events參數指定要監聽哪些類型的事件,在這個方法中,WatchService代表一個文件系統監聽服務,它負責監聽path代表的目錄下的文件變化,一旦使用register()方法完成註冊後,接下來就可以調用WatchService的如下三個方法來獲取被監聽目錄的文件變化事件:
- WatchKey poll():獲取下一個WatchKey,如果沒有WatchKey發生就立即返回null
- WatchKey poll(long timeout, TimeUnit unit):嘗試等待timeout時間去獲取下一個WatchKey
- WatchKey take(): 獲取下一個WatchKey,如果沒有WatchKey發生就一直等待
如果程序需要一直監控,則應該選擇使用take()方法,如果只需要監控指定時間則應該使用poll()方法
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;
public class WatchServiceTest
{
public static void main(String[] args)
throws Exception
{
// 獲取文件系統的WatchService對象
WatchService watchService = FileSystems.getDefault()
.newWatchService();
// 爲C:盤根路徑註冊監聽
Paths.get("C:/").register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
while (true)
{
// 獲取下一個文件改動事件
WatchKey key = watchService.take(); // ①
for (WatchEvent<?> event : key.pollEvents())
{
System.out.println(event.context() +" 文件發生了 "
+ event.kind()+ "事件!");
}
// 重設WatchKey
boolean valid = key.reset();
// 如果重設失敗,退出監聽
if (!valid)
{
break;
}
}
}
}
訪問文件屬性
NIO.2在java.nio.file.attribute包下提供了大量的工具類,通過這些工具類可以很簡單的讀寫文件屬性,工具類大致分爲兩大類:
- XxxAttribute View:代表某種文件屬性的視圖
- XxxAttributes: 代表某種文件屬性的集合,程序一般通過XxxAttributeView對象來獲取XxxAttributes
在這些工具類中,FileAttributeView是其他XxxAttributeView的父接口:
- AclFileAttributeView:通過AclFileAttributeView,開發者可以爲特定文件設置ACL(AccessControlList)及文件所有者屬性,它的getAcl()方法返回List< AclEntry >對象,該返回值代表了該文件的權限集,通過setAcl(List)方法可以修改該文件的ACL
- BasicFileAttributeView:它可以獲取或修改文件的基本屬性,包括文件的最後修改時間,最後訪問時間,創建時間,大小,是否爲目錄,是否爲符號鏈接等,它的readAttributes()方法返回一個BasicFileAttributes對象,對文件夾基本屬性的修改是通過BasicFileAttributes對象完成的
- DosFileAttributeView:它主要用於獲取或修改文件DOS相關屬性,例如文件是否只讀,是否隱藏,是否爲系統文件,是否是存檔文件等,它的readAttributes()方法返回一個DosFileAttributes對象,對這些屬性的修改其實是由DosFileAttributes對象來完成
- FileOwnerAttributeView:它主要用於獲取或修改文件的所有者,它的getOwner()方法返回一個UserPrincipal對象來代表文件所有者,也可以調用setOwner(UserPrincipal owner)方法來改變文件所有者
- PosixFileAttributeView:它主要用於獲取或修改POSIX(Portable Operatiing System Interface of INIX)屬性,它的readAttributes()方法返回一個PosixFileAttributes對象,該對象可用於獲取或修改文件的所有者、組所有者、訪問權限信息也就是chmod乾的事,這個View只在Unix、Linux等系統上有用
- UserDefinedFileAttributeView:用於設置自定義屬性
import java.io.*;
import java.util.*;
import java.nio.file.*;
import java.nio.*;
import java.nio.charset.*;
import java.nio.file.attribute.*;
public class AttributeViewTest
{
public static void main(String[] args)
throws Exception
{
// 獲取將要操作的文件
Path testPath = Paths.get("AttributeViewTest.java");
// 獲取訪問基本屬性的BasicFileAttributeView
BasicFileAttributeView basicView = Files.getFileAttributeView(
testPath, BasicFileAttributeView.class);
// 獲取訪問基本屬性的BasicFileAttributes
BasicFileAttributes basicAttribs = basicView.readAttributes();
// 訪問文件的基本屬性
System.out.println("創建時間:" + new Date(basicAttribs
.creationTime().toMillis()));
System.out.println("最後訪問時間:" + new Date(basicAttribs
.lastAccessTime().toMillis()));
System.out.println("最後修改時間:" + new Date(basicAttribs
.lastModifiedTime().toMillis()));
System.out.println("文件大小:" + basicAttribs.size());
// 獲取訪問文件屬主信息的FileOwnerAttributeView
FileOwnerAttributeView ownerView = Files.getFileAttributeView(
testPath, FileOwnerAttributeView.class);
// 獲取該文件所屬的用戶
System.out.println(ownerView.getOwner());
// 獲取系統中guest對應的用戶
UserPrincipal user = FileSystems.getDefault()
.getUserPrincipalLookupService()
.lookupPrincipalByName("guest");
// 修改用戶
ownerView.setOwner(user);
// 獲取訪問自定義屬性的FileOwnerAttributeView
UserDefinedFileAttributeView userView = Files.getFileAttributeView(
testPath, UserDefinedFileAttributeView.class);
List<String> attrNames = userView.list();
// 遍歷所有的自定義屬性
for (var name : attrNames)
{
ByteBuffer buf = ByteBuffer.allocate(userView.size(name));
userView.read(name, buf);
buf.flip();
String value = Charset.defaultCharset().decode(buf).toString();
System.out.println(name + "--->" + value);
}
// 添加一個自定義屬性
userView.write("發行者", Charset.defaultCharset()
.encode("一塊大洋"));
// 獲取訪問DOS屬性的DosFileAttributeView
DosFileAttributeView dosView = Files.getFileAttributeView(testPath,
DosFileAttributeView.class);
// 將文件設置隱藏、只讀
dosView.setHidden(true);
dosView.setReadOnly(true);
}
}