您当前的位置: 首页 >  Java

Charge8

暂无认证

  • 4浏览

    0关注

    416博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

Java7 Parh,Files和AIO中AsynchronousFileChannel

Charge8 发布时间:2019-09-10 17:38:28 ,浏览量:4

AIO (Asynchronous I/O) 异步非阻塞I/O 是 Java 1.7 之后的,,在java.nio包,AIO是是 NIO 的升级版本(所以AIO又叫NIO.2),提供了异步非堵塞的 IO 操作方式,异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。

AIO和NIO相比,同样有Channel和Buffer,但没有Selector。

Paths,Path与Files是在JDK7中新添加进来的,都在 java.nio.file包下面,使用时注意JDK版本

一、Paths类和Path接口

1、java.nio.file.Paths类

     作用:由专门的静态方法返回一个 Path对象,通过转换路径字符串或 URI。

     Paths类是 final修饰的,仅由两个static方法组成。

    • static Pathget(String first, String... more)

      将路径字符串或连接到路径字符串的字符串序列转换为 Path

      static Pathget(URI uri)

      将给定的URI转换为Path对象。

注意:文件系统的分隔符(Unix文件系统是 / ,Windows是 \,/也可以 )

           路径中的第一个部件可以是根部件,例如 / 或 C:\ 。以根部件开始的是绝对路径;否则就是相对路径。

  

    @Test
    public void testPaths() throws URISyntaxException {
        Path path = Paths.get("E:/java");
        System.out.println(path); // E:\java

        Path path2 = Paths.get("E:/java","/赵云.jpg");
        System.out.println(path2);// E:\java\赵云.jpg

        URI uri = new URI("file:///E:/java/赵云.jpg");
        Path path3 = Paths.get(uri);
        System.out.println(path3); //E:\java\赵云.jpg
    }

2、java.nio.file.Path接口    

Path实例表示文件系统中的路径。路径可以指向一个文件或目录。它通常表示系统相关的文件路径,可用于在文件系统中定位文件的对象。

在许多情况下,Path接口可以取代File类的使用。Path类包含一些对文件或目录处理的方法,但是和File类处理上也有一些区别。传送门:Java中 File类(文件、目录和文件过滤器)

1)判断路径以某一分层路径的开始或结束

    • booleanendsWith(Path other)

      测试此路径是否以给定的路径结束。

      booleanendsWith(String other)

      测试此路径是否以 Path结束,通过转换给定的路径字符串,完全按照 endsWith(Path)方法指定的方式构建。

      booleanstartsWith(Path other)

      测试此路径是否以给定的路径开始。

      booleanstartsWith(String other)

      测试此路径是否以 Path ,通过转换给定的路径字符串,按照 startsWith(Path)方法指定的方式构建。

    @Test
    public void testPaths() throws URISyntaxException {
        Path path = Paths.get("E:/java","/赵云.jpg");
        System.out.println(path);// E:\java\赵云.jpg

        System.out.println(path.endsWith("jpg"));//false  以某一分层路径
        System.out.println(path.endsWith("赵云.jpg"));//true
        System.out.println(path.startsWith(Paths.get("E:/java")));// true
    }

2)File 和 Path之间的转换,File和URI之间的转换

    • FiletoFile()

      返回表示此路径的File对象。

      URItoUri()

      返回一个URI来表示此路径。

    @Test
    public void testPaths() throws URISyntaxException {
        Path path = Paths.get("E:/java","/赵云.jpg");
        File file = path.toFile();
        URI uri = path.toUri();

        Path path1 = file.toPath();
        URI uri1 = file.toURI();
    }

3)其他的一些方法 

    • PathgetParent()

      返回 父路径 ,或 null如果此路径没有父。

      PathgetRoot()

      返回此路径的根组分作为 Path对象,或 null如果该路径不具有根组件。

      intcompareTo(Path other)

      比较两个抽象路径字典。

      booleanendsWith(Path other)

      测试,如果这个路径结束与给定的路径。

      PathgetFileName()

      将此路径表示的文件或目录的名称返回为 Path对象。

      FileSystemgetFileSystem()

      返回创建此对象的文件系统。

      booleanisAbsolute()

      告诉这条路是否是绝对的。

      Iteratoriterator()

      返回此路径的名称元素的迭代器。

      PathtoRealPath(LinkOption... options)

      返回现有文件的 真实路径。

 

二、java.nio.file.Files类

Files类提供了对文件、目录或者其他类型文件进行操作的静态方法。例如文件复制,移动,删除,读取文件内容,写入文件内容等。

Files类与Path实例一起配合使用。下面介绍一些常用的方法:

1、文件、目录判断与创建,删除

    • static booleanexists(Path path, LinkOption... options)

      测试一个文件是否存在。

      static booleannotExists(Path path, LinkOption... options)

      测试位于此路径的文件是否不存在。

      static longsize(Path path)

      返回一个文件的大小(以字节为单位)。

      static PathcreateDirectories(Path dir, FileAttribute... attrs)

      创建一个目录,通过创建所有不存在的父目录。

      static PathcreateDirectory(Path dir, FileAttribute... attrs)

      创建一个新目录。

      static PathcreateFile(Path path, FileAttribute... attrs)

      创建一个新的和空的文件,如果文件已经存在,失败了。

      static PathcreateLink(Path link, Path existing)

      创建一个新的链接(目录项)为现有的文件(可选操作)。

      static voiddelete(Path path)

      删除一个文件。

      static booleandeleteIfExists(Path path)

      如果它存在的话,删除一个文件。

      static booleanisDirectory(Path path, LinkOption... options)

      测试一个文件是否是一个目录。

      static booleanisExecutable(Path path)

      测试一个文件是否可执行。

      static booleanisHidden(Path path)

      告诉是否一个文件被认为是隐藏的。

      static booleanisReadable(Path path)

      测试一个文件是否可读。

      static booleanisWritable(Path path)

      测试文件是否可写。

java.nio.file.LinkOption枚举类定义如何处理符号链接的选项,但是只有一个字段NOFOLLOW_LINKS(不遵循符号链接),所以固定写法。

注意:

新文件创建:

如果该新文件已经存在,则会抛出java.nio.file.FileAlreadyExistsException。

如果该新文件的父目录不存在或者其他I/O错误,则会抛出IOException。

目录创建使用 createDirectories(),父目录不存在也会创建。

文件删除:

NoSuchFileException -如果文件不存在(可选特定异常)

DirectoryNotEmptyException -如果文件是一个目录,否则不能被删除,因为目录不是空的(可选特定异常)

目录删除:delete()方法只会删除一个文件和空目录。目录不为空时,抛异常。

    public static void main(String[] args) throws IOException {
        Path path = Paths.get("E:/java000/AIO/aa");

        if (Files.notExists(path)) {
            Files.createDirectories(path);
        }
        System.out.println(Files.isDirectory(path)); // true

        Path path2 = Paths.get("E:/java000/AIO/aa/text000.txt");
        if (Files.notExists(path2, LinkOption.NOFOLLOW_LINKS)) {
            if (!Files.isDirectory(path2)) {
                Files.createFile(path2);
            }
        }
        Files.deleteIfExists(path2);
        Files.delete(path); // DirectoryNotEmptyException: 
    }

2、文件复制与移动

    • static longcopy(InputStream in, Path target, CopyOption... options)

      将输入流中的所有字节复制到文件。

      static longcopy(Path source, OutputStream out)

      将文件中的所有字节复制到输出流。

      static Pathcopy(Path source, Path target, CopyOption... options)

      将文件复制到目标文件。

      static Pathmove(Path source, Path target, CopyOption... options)

      移动或重命名一个文件到目标文件。

    @Test
    public void testPaths() throws Exception {
        Path path = Paths.get("E:/java","/赵云image.jpg");
        FileInputStream inputStream = new FileInputStream(new File("E:/java/赵云.jpg"));
        // REPLACE_EXISTING - 如果目标文件已经存在,则替换
        Files.copy(inputStream, path, StandardCopyOption.REPLACE_EXISTING);

        Path destPath = Paths.get("E:/java/aa/aa赵云image.txt");
        Files.move(path, destPath, StandardCopyOption.REPLACE_EXISTING);
    }

注意:FileAlreadyExistsException -如果目标文件存在,并且没有指定 StandardCopyOption.REPLACE_EXISTING选项替换(可选特定异常)  

 

3、遍历一个文件夹目录获取文件(不包含嵌套文件夹中文件):newDirectoryStream

    • static DirectoryStreamnewDirectoryStream(Path dir)

      打开一个目录,返回一个DirectoryStream以遍历目录中的所有条目。

      static DirectoryStreamnewDirectoryStream(Path dir, DirectoryStream.Filter... attrs)

      打开或创建一个用于读取和/或写入的文件,返回一个异步文件通道来访问该文件。

      abstract Futureread(ByteBuffer dst, long position)

      从这个通道读取一个字节序列到给定的缓冲区,从给定的文件位置开始。

      abstract  voidread(ByteBuffer dst, long position, A attachment, CompletionHandler handler)

      从这个通道读取一个字节序列到给定的缓冲区,从给定的文件位置开始。

      abstract Futurewrite(ByteBuffer src, long position)

      从给定的缓冲区中从给定的缓冲区中写入一个字节序列,从给定的文件位置开始。

      abstract  voidwrite(ByteBuffer src, long position, A attachment, CompletionHandler handler)

      从给定的缓冲区中从给定的缓冲区中写入一个字节序列,从给定的文件位置开始。

      abstract longsize()

      返回此通道文件的当前大小。

OpenOption参数是可选的,表示打开文件的方式 。如:StandardOpenOption.READ,StandardOpenOption.WRITE等。

2、读数据

方式一:通过 Future 读数据

        AsynchronousFileChannel fileChannel = null;
        try {
            Path path = Paths.get("D:/E/AIO/aa/text.txt");
            // 缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

            // 1.创建AsynchronousFileChannel
            fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
            // 2.读数据
            Future future = fileChannel.read(byteBuffer, 0);
            // 在调用 read() 之后,循环调用 Future 的 isDone() 方法直到返回 true,则数据读完了。
            while (!future.isDone()) {
            }
            // 3.写数据
            System.out.println(new String(byteBuffer.array()));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭资源
            if (fileChannel != null) {
                try {
                    fileChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

read()接受一个ByteBuffer作为第一个参数,数据会被读取到ByteBuffer中。第二个参数是开始读取数据的位置。

read()方法会立刻返回,即使读操作没有完成。我们可以通过isDone()方法检查操作是否完成。一直循环,直到返回的isDone()方法返回true。循环操作比较低效,需要等到读取操作完成之后才会执行。

这里读取操作完成后,数据读取到ByteBuffer中,然后把缓冲区中的字符串打印。 

方式二:通过 CompletionHandler 读数据

    public static void main(String[] args) {
        AsynchronousFileChannel fileChannel = null;
        try {
            Path path = Paths.get("D:/E/AIO/aa/text.txt");
            // 缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

            // 1.创建AsynchronousFileChannel
            fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
            // 2.读数据
            fileChannel.read(byteBuffer, 0, byteBuffer, new CompletionHandler() {
                /**
                 * 从通道中把数据读到缓冲区完成之后,会调用它
                 * @param result - 读取得字节数
                 * @param attachment
                 */
                @Override
                public void completed(Integer result, ByteBuffer attachment) {
                    System.out.println("result: " + result);
                    System.out.println(new String(attachment.array()));
                }

                @Override
                public void failed(Throwable exc, ByteBuffer attachment) {

                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭资源
            if (fileChannel != null) {
                try {
                    fileChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        // 给显示留一些时间,实际项目可以删除
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

这里,一旦读取完成,将会触发CompletionHandler的completed()方法,并传入一个Integer和ByteBuffer。第一个参数Integer表示的是读取到的字节数大小。第二个参数ByteBuffer也可以换成其他合适的对象方便数据写入。 如果读取操作失败了,那么会触发failed()方法。  

3、写数据

方式一:通过 Future 写数据

    public static void main(String[] args) {
        AsynchronousFileChannel fileChannel = null;
        try {
            Path path = Paths.get("D:/E/AIO/aa/text1.txt");
            if(Files.notExists(path)){
                Files.createFile(path);
            }
            // 缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            byteBuffer.put("test data".getBytes());
            byteBuffer.flip();

            // 1.创建AsynchronousFileChannel
            fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
            // 2.写数据
            Future future = fileChannel.write(byteBuffer, 0);
            // isDone() 方法直到返回 true,则数据写完了。
            while (!future.isDone()) {
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭资源
            if (fileChannel != null) {
                try {
                    fileChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

首先把文件已写方式打开,然后把缓冲区的数据写入。最后检查一下是否写入完成。 注意:文件必须是已经存在的,否则会抛出一个java.nio.file.NoSuchFileException。

方式二:通过 CompletionHandler 写数据

    public static void main(String[] args) {
        AsynchronousFileChannel fileChannel = null;
        try {
            Path path = Paths.get("D:/E/AIO/aa/text1.txt");
            if(Files.notExists(path)){
                Files.createFile(path);
            }
            // 缓冲区
            ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
            byteBuffer.put("test data123121".getBytes());
            byteBuffer.flip();

            // 1.创建AsynchronousFileChannel
            fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE);
            // 2.写数据
            fileChannel.write(byteBuffer, 0, byteBuffer, new CompletionHandler() {
                @Override
                public void completed(Integer result, ByteBuffer attachment) {
                    System.out.println("result: " + result);
                    System.out.println(new String(attachment.array()));
                }

                @Override
                public void failed(Throwable exc, ByteBuffer attachment) {

                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4.关闭资源
            if (fileChannel != null) {
                try {
                    fileChannel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        // 给显示留一些时间,实际项目可以删除
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

当数据写入完成后completed()会被调用,如果失败了那么failed()会被调用。

 

参考文章:异步文件通道 AsynchronousFileChannel

ends~

 

关注
打赏
1661676204
查看更多评论
立即登录/注册

微信扫码登录

0.0522s