• Java NIO2 File API介绍


    Introduction to the Java NIO2 File API

    GitHub

    NIO2中的文件API是Java 7附带的Java平台的主要新功能之一,特别是新的文件系统API的一个子集以及Path APIs。

    在用户的主目录(home directory)下操作,使得对于所有的操作系统都是有效的。[内部是如何实现的呢???]

    private static String HOME = System.getProperty("user.home");

    Files类是Java.nio.file包的一个主要入口。这个类提供了丰富的APIs来读,写,操纵文件和目录。Files类方法在Path对象的实例上起作用。

    1.检查一个文件或目录

    我们可以让一个Path实例表示文件系统中的一个文件或目录。它所指向的那个文件或目录是否存在,是否可访问,可以通过文件操作来确认。

    每当我们使用术语File时,除非另有说明,否则我们将指代File和目录。

    检查一个File是否存在,使用exists

    @Test
    public void givenExistentPath_whenConfirmsFileExists_thenCorrect() {
        Path p = Paths.get(HOME);
     
        assertTrue(Files.exists(p));
    }

    检查一个File不存在,使用notExists

    @Test
    public void givenNonexistentPath_whenConfirmsFileNotExists_thenCorrect() {
        Path p = Paths.get(HOME + "/inexistent_file.txt");
     
        assertTrue(Files.notExists(p));
    }

    检查一个File是标准文件,像myfile.txt,还是只是个目录,使用isRegularFile :

    @Test
    public void givenDirPath_whenConfirmsNotRegularFile_thenCorrect() {
        Path p = Paths.get(HOME);
     
        assertFalse(Files.isRegularFile(p));
    }

    可以检查文件的权限。检查一个文件是否可读,使用isReadable :

    @Test
    public void givenExistentDirPath_whenConfirmsReadable_thenCorrect() {
        Path p = Paths.get(HOME);
     
        assertTrue(Files.isReadable(p));
    }

    检查一个文件是否可写,使用isWritable :

    @Test
    public void givenExistentDirPath_whenConfirmsWritable_thenCorrect() {
        Path p = Paths.get(HOME);
     
        assertTrue(Files.isWritable(p));
    }

    检查一个文件是否可执行,使用isExecutable :

    @Test
    public void givenExistentDirPath_whenConfirmsExecutable_thenCorrect() {
        Path p = Paths.get(HOME);
        assertTrue(Files.isExecutable(p));
    }

    有两个paths,可以检查它们在底层文件系统上是否指向相同的文件。

    @Test
    public void givenSameFilePaths_whenConfirmsIsSame_thenCorrect() {
        Path p1 = Paths.get(HOME);
        Path p2 = Paths.get(HOME);
     
        assertTrue(Files.isSameFile(p1, p2));
    }

    2.创建Files

    文件系统API提供了用于创建文件的单行操作。要创建一个标准文件,使用createFile,传给它代表我们要创建的文件的path对象。

    在path中的所有名字元素(name elements)除了文件名外必须存在。否则会得到一个IOException。

    @Test
    public void givenFilePath_whenCreatesNewFile_thenCorrect() {
        String fileName = "myfile_" + UUID.randomUUID().toString() + ".txt";
        Path p = Paths.get(HOME + "/" + fileName);
        assertFalse(Files.exists(p));
     
        Files.createFile(p);
     
        assertTrue(Files.exists(p));
    }

    要创建目录,使用createDirectory :

    @Test
    public void givenDirPath_whenCreatesNewDir_thenCorrect() {
        String dirName = "myDir_" + UUID.randomUUID().toString();
        Path p = Paths.get(HOME + "/" + dirName);
        assertFalse(Files.exists(p));
     
        Files.createDirectory(p);
     
        assertTrue(Files.exists(p));
        assertFalse(Files.isRegularFile(p));
        assertTrue(Files.isDirectory(p));
    }

    这个操作也要求path中的所有名字元素都存在。如果不是的话,也会得到IOException

    @Test(expected = NoSuchFileException.class)
    public void givenDirPath_whenFailsToCreateRecursively_thenCorrect() {
        String dirName = "myDir_" + UUID.randomUUID().toString() + "/subdir";
        Path p = Paths.get(HOME + "/" + dirName);
        assertFalse(Files.exists(p));
     
        Files.createDirectory(p);
    }

     如果想仅一次调用就创建一个层次的目录结构,使用createDirectories。和之前的操作不一样,当它在path中遇到丢失的名字元素时,它不会抛出IOException,它会递归创建它们直到最后一个元素:

    @Test
    public void givenDirPath_whenCreatesRecursively_thenCorrect() {
        Path dir = Paths.get(
          HOME + "/myDir_" + UUID.randomUUID().toString());
        Path subdir = dir.resolve("subdir");
        assertFalse(Files.exists(dir));
        assertFalse(Files.exists(subdir));
     
        Files.createDirectories(subdir);
     
        assertTrue(Files.exists(dir));
        assertTrue(Files.exists(subdir));
    }

    3.创建临时Files

    许多应用在它们运行时会在文件系统中创建临时文件的踪迹。结果大部分文件系统有一个专用目录来存储这样的应用产生的临时文件。

    新的文件系统API为这个目的提供了特定的操作。createTempFile API就执行这个操作。它要传入的参数时一个path对象,一个文件前缀,一个文件后缀。

    @Test
    public void givenFilePath_whenCreatesTempFile_thenCorrect() {
        String prefix = "log_";
        String suffix = ".txt";
        Path p = Paths.get(HOME + "/");
     
        Files.createTempFile(p, prefix, suffix);
             
        assertTrue(Files.exists(p));
    }

    这些参数对于创建临时文件的需求是足够了的。但是,如果要指定文件特定的属性,还有第四个可变参数的参数。

    上面的测试在HOME目录创建了一个临时文件,分别插入和追加了提供的前后缀字符串。将会得到文件如:log_8821081429012075286.txt。

    这个长的数字串是系统生成的。

    然而,如果不提供前后缀,文件名就只包含长数字串和默认的.tmp扩展名。

    @Test
    public void givenPath_whenCreatesTempFileWithDefaults_thenCorrect() {
        Path p = Paths.get(HOME + "/");
     
        Files.createTempFile(p, null, null);
             
        assertTrue(Files.exists(p));
    }

    生成的文件像:8600179353689423985.tmp

    如果path,前缀,后缀都不提供,这个操作将使用默认的输出。被创建文件的默认位置就是文件系统提供的临时文件目录:

    @Test
    public void givenNoFilePath_whenCreatesTempFileInTempDir_thenCorrect() {
        Path p = Files.createTempFile(null, null);
     
        assertTrue(Files.exists(p));
    }

    Windows中,默认就像:C:UsersuserAppDataLocalTemp6100927974988978748.tmp.

    通过使用createTempDirectory而不是createTempFile,上面所有的操作可用于创建目录而不是标准文件。

    4.删除File

    为了删除一个文件,使用delete API。为了清晰起见,下面的测试首先确保文件已经不存在,然后创建它并且确认现在它存在了,最后删掉它并确认它不再存在了。

    @Test
    public void givenPath_whenDeletes_thenCorrect() {
        Path p = Paths.get(HOME + "/fileToDelete.txt");
        assertFalse(Files.exists(p));
        Files.createFile(p);
        assertTrue(Files.exists(p));
     
        Files.delete(p);
     
        assertFalse(Files.exists(p));
    }

    如果文件不存在文件系统中,delete操作抛出IOException。

    @Test(expected = NoSuchFileException.class)
    public void givenInexistentFile_whenDeleteFails_thenCorrect() {
        Path p = Paths.get(HOME + "/inexistentFile.txt");
        assertFalse(Files.exists(p));
     
        Files.delete(p);
    }

    使用deleteIfExists可以避免这个场景,如果文件不存在它会默默地失败。当多个线程执行这个操作并且我们不想要失败信息时很有用。因为一个线程比当前失败的线程更早执行操作。[???]

    @Test
    public void givenInexistentFile_whenDeleteIfExistsWorks_thenCorrect() {
        Path p = Paths.get(HOME + "/inexistentFile.txt");
        assertFalse(Files.exists(p));
     
        Files.deleteIfExists(p);
    }

    处理目录而不是标准文件时,要记住,delete操作默认不会递归操作。如果目录不是空的,就会失败抛出IOException。

    @Test(expected = DirectoryNotEmptyException.class)
    public void givenPath_whenFailsToDeleteNonEmptyDir_thenCorrect() {
        Path dir = Paths.get(
          HOME + "/emptyDir" + UUID.randomUUID().toString());
        Files.createDirectory(dir);
        assertTrue(Files.exists(dir));
     
        Path file = dir.resolve("file.txt");
        Files.createFile(file);
     
        Files.delete(dir);
     
        assertTrue(Files.exists(dir));
    }

    5.复制Files

    使用copy API复制一个文件或目录:

    @Test
    public void givenFilePath_whenCopiesToNewLocation_thenCorrect() {
        Path dir1 = Paths.get(
          HOME + "/firstdir_" + UUID.randomUUID().toString());
        Path dir2 = Paths.get(
          HOME + "/otherdir_" + UUID.randomUUID().toString());
     
        Files.createDirectory(dir1);
        Files.createDirectory(dir2);
     
        Path file1 = dir1.resolve("filetocopy.txt");
        Path file2 = dir2.resolve("filetocopy.txt");
     
        Files.createFile(file1);
     
        assertTrue(Files.exists(file1));
        assertFalse(Files.exists(file2));
     
        Files.copy(file1, file2);
     
        assertTrue(Files.exists(file2));
    }

    目标文件存在的话,复制就失败。除非指定了REPLACE_EXISTING。

    @Test(expected = FileAlreadyExistsException.class)
    public void givenPath_whenCopyFailsDueToExistingFile_thenCorrect() {
        Path dir1 = Paths.get(
          HOME + "/firstdir_" + UUID.randomUUID().toString());
        Path dir2 = Paths.get(
          HOME + "/otherdir_" + UUID.randomUUID().toString());
     
        Files.createDirectory(dir1);
        Files.createDirectory(dir2);
     
        Path file1 = dir1.resolve("filetocopy.txt");
        Path file2 = dir2.resolve("filetocopy.txt");
     
        Files.createFile(file1);
        Files.createFile(file2);
     
        assertTrue(Files.exists(file1));
        assertTrue(Files.exists(file2));
     
        Files.copy(file1, file2);
     
        Files.copy(file1, file2, StandardCopyOption.REPLACE_EXISTING);
    }

    复制目录时,内容是不会递归地复制的。

    比如:/baeldung包含/articles.db/authors.db文件复制/baeldung 到新位置将会创建一个空的目录。

    6.移动Files

    使用move API移动文件或目录。它在很大程度上与复制操作相似。如果复制操作类似于基于GUI的系统中的复制和粘贴操作,则移动类似于剪切和粘贴操作:

    @Test
    public void givenFilePath_whenMovesToNewLocation_thenCorrect() {
        Path dir1 = Paths.get(
          HOME + "/firstdir_" + UUID.randomUUID().toString());
        Path dir2 = Paths.get(
          HOME + "/otherdir_" + UUID.randomUUID().toString());
     
        Files.createDirectory(dir1);
        Files.createDirectory(dir2);
     
        Path file1 = dir1.resolve("filetocopy.txt");
        Path file2 = dir2.resolve("filetocopy.txt");
        Files.createFile(file1);
     
        assertTrue(Files.exists(file1));
        assertFalse(Files.exists(file2));
     
        Files.move(file1, file2);
     
        assertTrue(Files.exists(file2));
        assertFalse(Files.exists(file1));
    }

    如果目标文件存在,移动操作将失败,除非REPLACE_EXISTING选项被指定了,就像我们对复制操作所做的一样:

    @Test(expected = FileAlreadyExistsException.class)
    public void givenFilePath_whenMoveFailsDueToExistingFile_thenCorrect() {
        Path dir1 = Paths.get(
          HOME + "/firstdir_" + UUID.randomUUID().toString());
        Path dir2 = Paths.get(
          HOME + "/otherdir_" + UUID.randomUUID().toString());
     
        Files.createDirectory(dir1);
        Files.createDirectory(dir2);
     
        Path file1 = dir1.resolve("filetocopy.txt");
        Path file2 = dir2.resolve("filetocopy.txt");
     
        Files.createFile(file1);
        Files.createFile(file2);
     
        assertTrue(Files.exists(file1));
        assertTrue(Files.exists(file2));
     
        Files.move(file1, file2);
     
        Files.move(file1, file2, StandardCopyOption.REPLACE_EXISTING);
     
        assertTrue(Files.exists(file2));
        assertFalse(Files.exists(file1));
    }
  • 相关阅读:
    [编程题] 微信红包
    MYSQL实现主从复制
    有关windows系统的EXE和DLL文件说法错误
    Http错误代码
    一步步优化JVM四:决定Java堆的大小以及内存占用
    一步步优化JVM三:GC优化基础
    一步步优化JVM二:JVM部署模型和JVM Runtime
    一步步优化JVM一:概述、方法及需求
    排查Java线上服务故障的方法和实例分析
    【转】Zookeeper-Watcher机制与异步调用原理
  • 原文地址:https://www.cnblogs.com/niaomingjian/p/8551706.html
Copyright © 2020-2023  润新知