目 录CONTENT

文章目录

NIO与FileSystem

FatFish1
2025-01-04 / 0 评论 / 0 点赞 / 60 阅读 / 0 字 / 正在检测是否收录...

JDK7开始基于NIO提供了一套新的FileSystem逻辑,这套逻辑的核心代码包括:

  • Files类:一个不同FileSystem系统下的统一工具类,提供了一套各种文件系统通用的方法,底层通过判断所处的文件系统调用对应的实现

  • Path类:可以针对不同的文件系统定义不同的Path,例如在windows系统下游WindowsPath实现,在sftp系统下有SftpPath实现(apache.sshd的实现)

  • FileSystemProvider:根据不同的Path判断当前所处的文件系统类型,例如WindowsFileSystemProvider

Nio的文件系统

Files

Files类的设计理念

Files类是一个不同FileSystem系统下的统一工具类,提供了一套各种文件系统通用的方法,底层通过判断所处的文件系统调用对应的实现

以一个通用方法为例:

public static boolean exists(Path path, LinkOption... options) {
    if (options.length == 0) {
        FileSystemProvider provider = provider(path);
        if (provider instanceof ExtendedFileSystemProvider)
            return ((ExtendedFileSystemProvider)provider).exists(path);
    }
    try {
        if (followLinks(options)) {
            provider(path).checkAccess(path);
        } else {
            // attempt to read attributes without following links
            readAttributes(path, BasicFileAttributes.class,
                               LinkOption.NOFOLLOW_LINKS);
        }
        // file exists
        return true;
    } catch (IOException x) {
        // does not exist or unable to determine if file exists
        return false;
    }
}

Files#exits方法是一个各个场景通用的判断方法,在这个方法中首先根据Path类型获取对于的FileSystemProvider,然后调用provider提供的对应方法

if (followLinks(options)) {
    provider(path).checkAccess(path);
} else {
    // attempt to read attributes without following links
    readAttributes(path, BasicFileAttributes.class,
                   LinkOption.NOFOLLOW_LINKS);
}
public static <A extends BasicFileAttributes> A readAttributes(Path path, Class<A> type, LinkOption... options) throws IOException {
    return provider(path).readAttributes(path, type, options);
}

这里可以看到两个流程都是首先基于provider方法获取FileSystemProvider,然后调用具体Provider的实现方法

因此我们可以得到一个结论,想要自行实现一套FileSystem只需要实现对应的Path和FileSystemProvider,然后基于Files类调用公共能力就可以实现

这一套逻辑典型的实现就是apache.sshd

provider

private static FileSystemProvider provider(Path path) {
    return path.getFileSystem().provider();
}

根据这一方法可以看出来,FileSystemProvider被封装在FileSystem中,而FileSystem又被封装在Path中

newInputStream

根据其注释,我们可以得知两点:

  • Opens a file, returning an input stream to read from the file. The stream will not be buffered,即这个流是一个非缓冲流

  • The stream will be safe for access by multiple concurrent threads,即这个流是一个线程安全的流

public static InputStream newInputStream(Path path, OpenOption... options) throws IOException {
    return provider(path).newInputStream(path, options);
}

这里调用FileSystemProvider#newInputStream方法,以windows系统为例可以看下默认实现补链接

众所周知,在BIO里面也是有对应的FileInputStream存在的,为什么NIO里面还要再搞一套流出来呢?结合下游方法和注释,可以大概推测JDK开发者的用意:

  • 将老的BIO中文件流转换成非阻塞的NIO模式,既兼容了老的流处理逻辑,又提高了吞吐量和大文件处理效率

  • 线程安全

newByteChannel

public static SeekableByteChannel newByteChannel(Path path, OpenOption... options)
    throws IOException
{
    Set<OpenOption> set;
    if (options.length == 0) {
        set = Collections.emptySet();
    } else {
        set = new HashSet<>();
        Collections.addAll(set, options);
    }
    return newByteChannel(path, set);
}

返回的是一个SeekableByteChannel,它实现自ReadableByteChannel,可以把ReadableByteChannel想象成前面nio中SocketChannel和ServerSocketChannel的兄弟

http://www.chymfatfish.cn/archives/nio#socketchannel

根据注释,ReadableByteChannel的作用是提供一个传输字节流的,且可读的Channel,它的核心能力是把BIO模式转换成非阻塞的NIO模式

而SeekableByteChannle在ReadableByteChannel的基础上实现位置的维护和更改能力,我们的老朋友FileChannel及其实现类FileChannelImpl就是它的实现类

http://www.chymfatfish.cn/archives/nio#filechannel

FileSystemProvider

newInputStream

默认实现如下:

public InputStream newInputStream(Path path, OpenOption... options)
    throws IOException
{
    ……
    // 构造ReadableByteChannel
    ReadableByteChannel rbc = Files.newByteChannel(path, options);
    ……
    // 构造流
    return Channels.newInputStream(rbc);
}

该方法可以大概分为两步:

  • 构造ReadableByteChannel,这里可以参考Files#newByteChannel 方法补链接

  • 基于ReadableByteChannel构造InputStream

向下找到Channels#newInputStream方法

public static InputStream newInputStream(ReadableByteChannel ch) {
    Objects.requireNonNull(ch, "ch");
    return new ChannelInputStream(ch);
}

这里是基于ReadableByteChannel构造的ChannelInputStream

这个ChannelInputStream实现自InputStream,显然是BIO的产物,但是由于其中封装了ReadableByteChannel属性,让它能够桥接BIO和NIO,并且线程安全

protected final ReadableByteChannel ch;

0

评论区