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)

  1. Path path1 = Paths.get("c:/abc");
  2. //或者
  3. 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();

  1. <a name="thzQx"></a>
  2. ### 2) 常用的路径操作
  3. - 获取当前文件或目录的名字: `String getFileName()`
  4. - 获取相关路径:
  5. - 父路径:Path getParent()
  6. - 绝对路径:Path toAbsolutePath()
  7. - 相对路经:Path relativize(Path other)
  8. - 合并路径:Path resolve(String other)
  9. - 转换操作:
  10. - 转换成字符串:String toString()
  11. - 精简Path对象:Path normalize()
  12. - 转换成File对象:File toFile()
  13. <a name="EpNpH"></a>
  14. # 2. 文件相关操作
  15. <a name="m3Dzs"></a>
  16. ## A. java.nio.file.Files
  17. Java NIO把原有的File类拆解出来, 用Files替换File类的文件操作。Files是对File类的升级,使我们在操作多个文件时不必要定义每个文件实例。Files工具类需要使用Path类。
  18. <a name="efbjs"></a>
  19. ### 常用的文件操作:
  20. - 检测文件或目录:`Files.exists(Path)` 和 `Files.notExists(Path)`
  21. 注意:!exist() 与notExists()并不完全相等,exist可能有三种状态:如果不存在或者安全校验不通过则返回false,如果返回true则表示文件确实存在且有权限。notExists()检测类似,对于没有通过安全校验的也会返回false;当exists与notExists同时返回false时,说明文件不可以验证(即无权限)。
  22. - 判断文件是否有相应权限:
  23. - Files.isRegularFile(path)
  24. - Files.isReadable(path)
  25. - Files.isExecutable(path);
  26. - 创建文件:
  27. - Files.createFile(path); 如果已存在则抛出异常,文件属性为默认。
  28. - Files.createSymbolicLink(path_target, path_source):创建一个软连接文件。
  29. - 删除文件和目录:
  30. - `delete`和`deleteIfExists`两个方法均可删除文件,前者尝试删除的文件如果不存在则会抛出异常。
  31. - 如果文件是软连接,则只删除连接文件而不会删除target文件
  32. - 如果path为文件夹,则文件夹需要为空,否则删除失败(IOException)
  33. - 文件和目录复制:
  34. - `copy(Path source, Path target, CopyOption...)` 方法可以复制文件,不过,需要注意CopyOption的相关选项。
  35. - 如下为CopyOption选项列表:
  36. - **REPLACE_EXISTING**:如果目标文件已经存在,则直接覆盖;如果目标文件是个软连接,则软连接文件本身被覆盖(而非连接文件的target文件);如果复制的是目录,且目标目录不为空时,则会抛出异常(DirectoryNotEmptyException),稍后介绍“递归复制目录树和文件”。此参数通常必选。复制目录时,目标目录会自动创建,源目录中如果有文件,则不会复制文件,只会创建空的目标目录。source和target,要么同时是目录、要么同时是文件。
  37. - **COPY_ATTRIBUTES**:复制文件时,也同时复制目标文件的属性(metadata)
  38. - **NOFOLLOW_LINKS**:当copy一个软连接文件时,默认将会复制target文件,如果只想复制软连接文件而不是target内容,可以指定NOFOLLOW_LINKS选项。
  39. - 文件和目录的移动:
  40. - `move(Path source, Path target, CopyOption...)`基本原则同copy。需要注意,如果是目录,目录中包含文件时也可以移动的(这可能依赖于平台),子目录也一起移动,但是目标目录必须为空(DirectoryNotEmptyException)。基本语义同“mv -rf”,目标目录不需要提前创建,move结束后,源目录将不存在。
  41. - 支持两种选项:
  42. - **REPLACE_EXISTING**:如果目标文件已存在,则覆盖;如果目标文件是软连接,则连接文件被覆盖但是其指向不会受影响。
  43. - **ATOMIC_MOVE**:原子复制,需要平台的文件系统支持(不支持则抛出异常),指定此参数时其他选项将被忽略;如果文件不能被原子复制(或者替换),则会抛出AtomicMoveNotSupportedException。
  44. - 使用文件:
  45. - 读取文件内容:
  46. - List<String> readAllLines(Path path, Charset cs):
  47. `List<String> lines = Files.readAllLines(Paths.get("/data/web.log"),Charset.forName("utf-8"));`
  48. - BufferredReader newBufferedReader(Path path, Charset cs)
  49. ```java
  50. try (BufferedReader reader = Files.newBufferedReader(Paths.get("/data/web.log"))) {
  51. while (true) {
  52. String line = reader.readLine();
  53. if (line == null) {
  54. break;
  55. }
  56. System.out.println(line);
  57. }
  58. } catch (IOException e) {
  59. //
  60. }
  • 读取文件内容:
    • void write(Path path, List lines, Charset cs, StandardOpenOption …):

Files.write(Paths.get("/data/web-1.log"),lines,Charset.forName("utf-8"),
StandardOpenOption.APPEND, StandardOpenOption.CREATE));

  1. - 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);
}