JAVA7+对NIO进行了增强,主要在对文件操作部分做了大量的改进。它体现在,将File操作进行分离、封装、改进,最终形成Path(Paths)、Files、FileSystem(FileSystems)三个主要类。
1. 路径相关操作
文件系统都是Tree或者层级结构来组织文件的,任何一个节点可以是一个目录或者一个文件,在NIO2中称为Path。Path代表的是文件或目录在文件系统中的位置。
A. 路径的表示方式:
- 绝对路径:从一个文件系统root目录开始的路径
相对路径:相对路径相对于某个位置,在eclipse下是指当前项目下
B. java.nio.file.Paths
Paths类仅由静态方法组成,通过转换路径字符串返回
Path对象。将若干个路径字符串连接起来生成一个Path对象:
static Path get(String first, String... more)
Path path1 = Paths.get("c:/abc");//或者Path path2 = Paths.get("c:","abc");
- 将给定的URI转换为Path对象
static Path get(URI uri)
- C. java.nio.file.Path
Path就是取代File的,用于来表示文件系统中的node(文件和文件夹)。可以有多种方法来构造一个Path对象来表示一个文件路径,或者一个文件:
- 该接口的实现是不可变且安全的,可供多个并行线程使用。
1) 创建Path对象:
- 通过字符串转换:Paths.get(String … strs)
- 通过合并路径:path.resolve(String subPathString)
- 通过File转换:file.toPath() ```java //absolution path Path path = Paths.get(“C:/rafaelnadal/tournaments/2009/BNP.txt”); //获取home directory下路径 Path path = Paths.get(System.getProperty(“user.home”), “downloads”, “game.exe”); //relative path Path path = Paths.get(,”tournaments/2009/BNP.txt”);
//合并路径: Path base = Paths.get(“C:/rafaelnadal/tournaments/2009”); Path path_1 = base.resolve(“BNP.txt”);
//通过File转换: File file = new (“C:\demo.txt”); Path path = file.toPath();
<a name="thzQx"></a>### 2) 常用的路径操作- 获取当前文件或目录的名字: `String getFileName()`- 获取相关路径:- 父路径:Path getParent()- 绝对路径:Path toAbsolutePath()- 相对路经:Path relativize(Path other)- 合并路径:Path resolve(String other)- 转换操作:- 转换成字符串:String toString()- 精简Path对象:Path normalize()- 转换成File对象:File toFile()<a name="EpNpH"></a># 2. 文件相关操作<a name="m3Dzs"></a>## A. java.nio.file.FilesJava NIO把原有的File类拆解出来, 用Files替换File类的文件操作。Files是对File类的升级,使我们在操作多个文件时不必要定义每个文件实例。Files工具类需要使用Path类。<a name="efbjs"></a>### 常用的文件操作:- 检测文件或目录:`Files.exists(Path)` 和 `Files.notExists(Path)`注意:!exist() 与notExists()并不完全相等,exist可能有三种状态:如果不存在或者安全校验不通过则返回false,如果返回true则表示文件确实存在且有权限。notExists()检测类似,对于没有通过安全校验的也会返回false;当exists与notExists同时返回false时,说明文件不可以验证(即无权限)。- 判断文件是否有相应权限:- Files.isRegularFile(path)- Files.isReadable(path)- Files.isExecutable(path);- 创建文件:- Files.createFile(path); 如果已存在则抛出异常,文件属性为默认。- Files.createSymbolicLink(path_target, path_source):创建一个软连接文件。- 删除文件和目录:- `delete`和`deleteIfExists`两个方法均可删除文件,前者尝试删除的文件如果不存在则会抛出异常。- 如果文件是软连接,则只删除连接文件而不会删除target文件- 如果path为文件夹,则文件夹需要为空,否则删除失败(IOException)- 文件和目录复制:- `copy(Path source, Path target, CopyOption...)` 方法可以复制文件,不过,需要注意CopyOption的相关选项。- 如下为CopyOption选项列表:- **REPLACE_EXISTING**:如果目标文件已经存在,则直接覆盖;如果目标文件是个软连接,则软连接文件本身被覆盖(而非连接文件的target文件);如果复制的是目录,且目标目录不为空时,则会抛出异常(DirectoryNotEmptyException),稍后介绍“递归复制目录树和文件”。此参数通常必选。复制目录时,目标目录会自动创建,源目录中如果有文件,则不会复制文件,只会创建空的目标目录。source和target,要么同时是目录、要么同时是文件。- **COPY_ATTRIBUTES**:复制文件时,也同时复制目标文件的属性(metadata)- **NOFOLLOW_LINKS**:当copy一个软连接文件时,默认将会复制target文件,如果只想复制软连接文件而不是target内容,可以指定NOFOLLOW_LINKS选项。- 文件和目录的移动:- `move(Path source, Path target, CopyOption...)`基本原则同copy。需要注意,如果是目录,目录中包含文件时也可以移动的(这可能依赖于平台),子目录也一起移动,但是目标目录必须为空(DirectoryNotEmptyException)。基本语义同“mv -rf”,目标目录不需要提前创建,move结束后,源目录将不存在。- 支持两种选项:- **REPLACE_EXISTING**:如果目标文件已存在,则覆盖;如果目标文件是软连接,则连接文件被覆盖但是其指向不会受影响。- **ATOMIC_MOVE**:原子复制,需要平台的文件系统支持(不支持则抛出异常),指定此参数时其他选项将被忽略;如果文件不能被原子复制(或者替换),则会抛出AtomicMoveNotSupportedException。- 使用文件:- 读取文件内容:- List<String> readAllLines(Path path, Charset cs):`List<String> lines = Files.readAllLines(Paths.get("/data/web.log"),Charset.forName("utf-8"));`- BufferredReader newBufferedReader(Path path, Charset cs)```javatry (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) {//}
- 读取文件内容:
- void write(Path path, List
lines, Charset cs, StandardOpenOption …):
- void write(Path path, List
Files.write(Paths.get("/data/web-1.log"),lines,Charset.forName("utf-8"),StandardOpenOption.APPEND, StandardOpenOption.CREATE));
- BufferedWriter newBufferedWriter(Path path, Charset cs, StandardOpenOption ...):
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) {
//
}
3. 目录相关操作
A. 创建目录:
Path dir = Paths.get("/data/xyz");
Files.createDirectories(dir);
Files.createDirectory(dir);
其中createDirectory()方法是一个“严格校验”的方法,如果父路径不存在则会抛出异常,如果路径已经存在或者同名文件存在则会抛出异常,简单来说此方法只能创建最后一级目录(且此前不存在)。对于createDirectories()方法,比较兼容,会逐层校验并创建父路径,如果不存在则创建。
B. 遍历目录:
stream方式会遍历指定目录下的所有文件,包括文件、目录、连接、隐藏文件或者目录等。 但是它不能递归遍历子目录
Path dir = Paths.get("/data");
DirectoryStream<Path> stream = Files.newDirectoryStream(dir);
for (Path path : stream) {
System.out.println(path);
}
stream.close();
过滤器 filter和Glob文件过滤器:
Path dir = Paths.get("/data");
DirectoryStream<Path> stream = Files.newDirectoryStream(dir,path -> Files.isRegularFile(path));
for (Path path : stream) {
System.out.println(path);
}
stream.close();
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();
C. 递归遍历目录树
Path dir = Paths.get("/data/redis");
Stream<Path> stream = Files.walk(dir);
stream.forEach(path \-> {
System.out.println(path);
});
stream.close();
4. metadata管理
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("modified time:" + attributes.lastModifiedTime());
//修改系统更新属性
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类,否则会导致设置异常。
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
5. 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);
}
